diff options
Diffstat (limited to 'kdesu/client.cpp')
-rw-r--r-- | kdesu/client.cpp | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/kdesu/client.cpp b/kdesu/client.cpp new file mode 100644 index 000000000..ae97f0af8 --- /dev/null +++ b/kdesu/client.cpp @@ -0,0 +1,435 @@ +/* vi: ts=8 sts=4 sw=4 + * + * $Id$ + * + * This file is part of the KDE project, module kdesu. + * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org> + * + * This is free software; you can use this library under the GNU Library + * General Public License, version 2. See the file "COPYING.LIB" for the + * exact licensing terms. + * + * client.cpp: A client for kdesud. + */ + +#include <config.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <pwd.h> +#include <errno.h> +#include <string.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> + +#include <qglobal.h> +#include <qcstring.h> +#include <qfile.h> +#include <qregexp.h> + +#include <kdebug.h> +#include <kstandarddirs.h> +#include <kapplication.h> +#include <kde_file.h> + +#include "client.h" + +class KDEsuClient::KDEsuClientPrivate { +public: + QString daemon; +}; + +#ifndef SUN_LEN +#define SUN_LEN(ptr) ((socklen_t) (((struct sockaddr_un *) 0)->sun_path) \ + + strlen ((ptr)->sun_path)) +#endif + +KDEsuClient::KDEsuClient() +{ + sockfd = -1; +#ifdef Q_WS_X11 + QCString display(getenv("DISPLAY")); + if (display.isEmpty()) + { + kdWarning(900) << k_lineinfo << "$DISPLAY is not set\n"; + return; + } + + // strip the screen number from the display + display.replace(QRegExp("\\.[0-9]+$"), ""); +#else + QCString display("QWS"); +#endif + + sock = QFile::encodeName(locateLocal("socket", QString("kdesud_%1").arg(display))); + d = new KDEsuClientPrivate; + connect(); +} + + +KDEsuClient::~KDEsuClient() +{ + delete d; + if (sockfd >= 0) + close(sockfd); +} + +int KDEsuClient::connect() +{ + if (sockfd >= 0) + close(sockfd); + if (access(sock, R_OK|W_OK)) + { + sockfd = -1; + return -1; + } + + sockfd = socket(PF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) + { + kdWarning(900) << k_lineinfo << "socket(): " << perror << "\n"; + return -1; + } + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, sock); + + if (::connect(sockfd, (struct sockaddr *) &addr, SUN_LEN(&addr)) < 0) + { + kdWarning(900) << k_lineinfo << "connect():" << perror << endl; + close(sockfd); sockfd = -1; + return -1; + } + +#if !defined(SO_PEERCRED) || !defined(HAVE_STRUCT_UCRED) +# if defined(HAVE_GETPEEREID) + uid_t euid; + gid_t egid; + // Security: if socket exists, we must own it + if (getpeereid(sockfd, &euid, &egid) == 0) + { + if (euid != getuid()) + { + kdWarning(900) << "socket not owned by me! socket uid = " << euid << endl; + close(sockfd); sockfd = -1; + return -1; + } + } +# else +# ifdef __GNUC__ +# warning "Using sloppy security checks" +# endif + // We check the owner of the socket after we have connected. + // If the socket was somehow not ours an attacker will be able + // to delete it after we connect but shouldn't be able to + // create a socket that is owned by us. + KDE_struct_stat s; + if (KDE_lstat(sock, &s)!=0) + { + kdWarning(900) << "stat failed (" << sock << ")" << endl; + close(sockfd); sockfd = -1; + return -1; + } + if (s.st_uid != getuid()) + { + kdWarning(900) << "socket not owned by me! socket uid = " << s.st_uid << endl; + close(sockfd); sockfd = -1; + return -1; + } + if (!S_ISSOCK(s.st_mode)) + { + kdWarning(900) << "socket is not a socket (" << sock << ")" << endl; + close(sockfd); sockfd = -1; + return -1; + } +# endif +#else + struct ucred cred; + socklen_t siz = sizeof(cred); + + // Security: if socket exists, we must own it + if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &siz) == 0) + { + if (cred.uid != getuid()) + { + kdWarning(900) << "socket not owned by me! socket uid = " << cred.uid << endl; + close(sockfd); sockfd = -1; + return -1; + } + } +#endif + + return 0; +} + +QCString KDEsuClient::escape(const QCString &str) +{ + QCString copy = str; + int n = 0; + while ((n = copy.find("\\", n)) != -1) + { + copy.insert(n, '\\'); + n += 2; + } + n = 0; + while ((n = copy.find("\"", n)) != -1) + { + copy.insert(n, '\\'); + n += 2; + } + copy.prepend("\""); + copy.append("\""); + return copy; +} + +int KDEsuClient::command(const QCString &cmd, QCString *result) +{ + if (sockfd < 0) + return -1; + + if (send(sockfd, cmd, cmd.length(), 0) != (int) cmd.length()) + return -1; + + char buf[1024]; + int nbytes = recv(sockfd, buf, 1023, 0); + if (nbytes <= 0) + { + kdWarning(900) << k_lineinfo << "no reply from daemon\n"; + return -1; + } + buf[nbytes] = '\000'; + + QCString reply = buf; + if (reply.left(2) != "OK") + return -1; + + if (result) + *result = reply.mid(3, reply.length()-4); + return 0; +} + +int KDEsuClient::setPass(const char *pass, int timeout) +{ + QCString cmd = "PASS "; + cmd += escape(pass); + cmd += " "; + cmd += QCString().setNum(timeout); + cmd += "\n"; + return command(cmd); +} + +int KDEsuClient::exec(const QCString &prog, const QCString &user, const QCString &options, const QCStringList &env) +{ + QCString cmd; + cmd = "EXEC "; + cmd += escape(prog); + cmd += " "; + cmd += escape(user); + if (!options.isEmpty() || !env.isEmpty()) + { + cmd += " "; + cmd += escape(options); + for(QCStringList::ConstIterator it = env.begin(); + it != env.end(); ++it) + { + cmd += " "; + cmd += escape(*it); + } + } + cmd += "\n"; + return command(cmd); +} + +int KDEsuClient::setHost(const QCString &host) +{ + QCString cmd = "HOST "; + cmd += escape(host); + cmd += "\n"; + return command(cmd); +} + +int KDEsuClient::setPriority(int prio) +{ + QCString cmd; + cmd.sprintf("PRIO %d\n", prio); + return command(cmd); +} + +int KDEsuClient::setScheduler(int sched) +{ + QCString cmd; + cmd.sprintf("SCHD %d\n", sched); + return command(cmd); +} + +int KDEsuClient::delCommand(const QCString &key, const QCString &user) +{ + QCString cmd = "DEL "; + cmd += escape(key); + cmd += " "; + cmd += escape(user); + cmd += "\n"; + return command(cmd); +} +int KDEsuClient::setVar(const QCString &key, const QCString &value, int timeout, + const QCString &group) +{ + QCString cmd = "SET "; + cmd += escape(key); + cmd += " "; + cmd += escape(value); + cmd += " "; + cmd += escape(group); + cmd += " "; + cmd += QCString().setNum(timeout); + cmd += "\n"; + return command(cmd); +} + +QCString KDEsuClient::getVar(const QCString &key) +{ + QCString cmd = "GET "; + cmd += escape(key); + cmd += "\n"; + QCString reply; + command(cmd, &reply); + return reply; +} + +QValueList<QCString> KDEsuClient::getKeys(const QCString &group) +{ + QCString cmd = "GETK "; + cmd += escape(group); + cmd += "\n"; + QCString reply; + command(cmd, &reply); + int index=0, pos; + QValueList<QCString> list; + if( !reply.isEmpty() ) + { + // kdDebug(900) << "Found a matching entry: " << reply << endl; + while (1) + { + pos = reply.find( '\007', index ); + if( pos == -1 ) + { + if( index == 0 ) + list.append( reply ); + else + list.append( reply.mid(index) ); + break; + } + else + { + list.append( reply.mid(index, pos-index) ); + } + index = pos+1; + } + } + return list; +} + +bool KDEsuClient::findGroup(const QCString &group) +{ + QCString cmd = "CHKG "; + cmd += escape(group); + cmd += "\n"; + if( command(cmd) == -1 ) + return false; + return true; +} + +int KDEsuClient::delVar(const QCString &key) +{ + QCString cmd = "DELV "; + cmd += escape(key); + cmd += "\n"; + return command(cmd); +} + +int KDEsuClient::delGroup(const QCString &group) +{ + QCString cmd = "DELG "; + cmd += escape(group); + cmd += "\n"; + return command(cmd); +} + +int KDEsuClient::delVars(const QCString &special_key) +{ + QCString cmd = "DELS "; + cmd += escape(special_key); + cmd += "\n"; + return command(cmd); +} + +int KDEsuClient::ping() +{ + return command("PING\n"); +} + +int KDEsuClient::exitCode() +{ + QCString result; + if (command("EXIT\n", &result) != 0) + return -1; + + return result.toLong(); +} + +int KDEsuClient::stopServer() +{ + return command("STOP\n"); +} + +static QString findDaemon() +{ + QString daemon = locate("bin", "kdesud"); + if (daemon.isEmpty()) // if not in KDEDIRS, rely on PATH + daemon = KStandardDirs::findExe("kdesud"); + + if (daemon.isEmpty()) + { + kdWarning(900) << k_lineinfo << "daemon not found\n"; + } + return daemon; +} + +bool KDEsuClient::isServerSGID() +{ + if (d->daemon.isEmpty()) + d->daemon = findDaemon(); + if (d->daemon.isEmpty()) + return false; + + KDE_struct_stat sbuf; + if (KDE_stat(QFile::encodeName(d->daemon), &sbuf) < 0) + { + kdWarning(900) << k_lineinfo << "stat(): " << perror << "\n"; + return false; + } + return (sbuf.st_mode & S_ISGID); +} + +int KDEsuClient::startServer() +{ + if (d->daemon.isEmpty()) + d->daemon = findDaemon(); + if (d->daemon.isEmpty()) + return -1; + + if (!isServerSGID()) { + kdWarning(900) << k_lineinfo << "kdesud not setgid!\n"; + } + + // kdesud only forks to the background after it is accepting + // connections. + // We start it via kdeinit to make sure that it doesn't inherit + // any fd's from the parent process. + int ret = kapp->kdeinitExecWait(d->daemon); + connect(); + return ret; +} |