diff --git a/src/ioslaves/file/fdreceiver.h b/src/ioslaves/file/fdreceiver.h --- a/src/ioslaves/file/fdreceiver.h +++ b/src/ioslaves/file/fdreceiver.h @@ -42,6 +42,7 @@ QSocketNotifier *m_readNotifier; int m_socketDes; int m_fileDes; + QString m_path; }; #endif diff --git a/src/ioslaves/file/fdreceiver.cpp b/src/ioslaves/file/fdreceiver.cpp --- a/src/ioslaves/file/fdreceiver.cpp +++ b/src/ioslaves/file/fdreceiver.cpp @@ -28,14 +28,20 @@ , m_readNotifier(nullptr) , m_socketDes(-1) , m_fileDes(-1) + , m_path(path) { + SocketAddress addr(m_path.toStdString()); + if (!addr.address()) { + std::cerr << "Invalid socket address" << std::endl; + return; + } + m_socketDes = ::socket(AF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0); if (m_socketDes == -1) { std::cerr << "socket error:" << strerror(errno) << std::endl; return; } - const SocketAddress addr(path.toStdString()); if (bind(m_socketDes, addr.address(), addr.length()) != 0 || listen(m_socketDes, 5) != 0) { std::cerr << "bind/listen error:" << strerror(errno) << std::endl; ::close(m_socketDes); @@ -52,6 +58,7 @@ if (m_socketDes >= 0) { ::close(m_socketDes); } + ::unlink(m_path.toStdString().c_str()); } bool FdReceiver::isListening() const @@ -63,11 +70,30 @@ { int client = ::accept(m_socketDes, NULL, NULL); if (client > 0) { - FDMessageHeader msg; - if (::recvmsg(client, msg.message(), 0) == 2) { - ::memcpy(&m_fileDes, CMSG_DATA(msg.cmsgHeader()), sizeof m_fileDes); + // Receive fd only if socket owner is root + bool acceptConnection = false; +#if defined(__linux__) + ucred cred; + socklen_t len = sizeof(cred); + if (getsockopt(client, SOL_SOCKET, SO_PEERCRED, &cred, &len) == 0 && cred.uid == 0) { + acceptConnection = true; + } +#elif defined(__FreeBSD__) || defined(__APPLE__) + uid_t uid; + gid_t gid; + if (getpeereid(m_socketDes, &uid, &gid) == 0 && uid == 0) { + acceptConnection = true; + } +#else +#error Cannot get socket credentials! +#endif + if (acceptConnection) { + FDMessageHeader msg; + if (::recvmsg(client, msg.message(), 0) == 2) { + ::memcpy(&m_fileDes, CMSG_DATA(msg.cmsgHeader()), sizeof m_fileDes); + } + ::close(client); } - ::close(client); } } diff --git a/src/ioslaves/file/file_unix.cpp b/src/ioslaves/file/file_unix.cpp --- a/src/ioslaves/file/file_unix.cpp +++ b/src/ioslaves/file/file_unix.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -69,7 +70,8 @@ static const QString socketPath() { - return QStringLiteral("org_kde_kio_file_helper_%1").arg(getpid()); + const QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); + return QStringLiteral("%1/org_kde_kio_file_helper_%2").arg(runtimeDir).arg(getpid()); } bool FileProtocol::privilegeOperationUnitTestMode() @@ -80,7 +82,17 @@ PrivilegeOperationReturnValue FileProtocol::tryOpen(QFile &f, const QByteArray &path, int flags, int mode) { - FdReceiver fdRecv(socketPath()); + const QString sockPath = socketPath(); + + // Privilege execution depends on error value EACCES or EPERM. + // Trying to delete a non-existent file sets errno to ENOENT + // causing the file operation to fail. To fix this cache errno's + // value and reset it after deleting socket file. + int err = errno; + QFile::remove(sockPath); + errno = err; + + FdReceiver fdRecv(sockPath); if (!fdRecv.isListening()) { return PrivilegeOperationReturnValue::failure(); } diff --git a/src/ioslaves/file/kauth/fdsender.cpp b/src/ioslaves/file/kauth/fdsender.cpp --- a/src/ioslaves/file/kauth/fdsender.cpp +++ b/src/ioslaves/file/kauth/fdsender.cpp @@ -24,13 +24,18 @@ FdSender::FdSender(const std::string &path) : m_socketDes(-1) { + SocketAddress addr(path); + if (!addr.address()) { + std::cerr << "Invalid socket address" << std::endl; + return; + } + m_socketDes = ::socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); if (m_socketDes == -1) { std::cerr << "socket error:" << strerror(errno) << std::endl; return; } - SocketAddress addr(path); if (::connect(m_socketDes, addr.address(), addr.length()) != 0) { std::cerr << "connection error:" << strerror(errno) << std::endl; ::close(m_socketDes); diff --git a/src/ioslaves/file/sharefd_p.h b/src/ioslaves/file/sharefd_p.h --- a/src/ioslaves/file/sharefd_p.h +++ b/src/ioslaves/file/sharefd_p.h @@ -22,6 +22,9 @@ #include #include #include +#include +#include +#include // fix SOCK_NONBLOCK for e.g. macOS #ifndef SOCK_NONBLOCK @@ -41,24 +44,22 @@ int length() const { - return sizeof addr; + return offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path); } const sockaddr *address() const { - return reinterpret_cast(&addr); + return (strlen(addr.sun_path) > 0) ? reinterpret_cast(&addr) : nullptr; } private: static sockaddr_un make_address(const std::string& path) { - sockaddr_un a{ AF_UNIX, {0}}; - std::string finalPath = "/tmp/" + path; -#ifdef __linux__ - ::strcpy(&a.sun_path[1], finalPath.c_str()); -#else - ::strcpy(a.sun_path, finalPath.c_str()); - ::unlink(finalPath.c_str()); -#endif + sockaddr_un a; + memset(&a, 0, sizeof(a)); + a.sun_family = AF_UNIX; + if (path.length() > 0 && path.length() < sizeof(a.sun_path)-1) { + ::strncpy(a.sun_path, path.c_str(), sizeof(a.sun_path)-1); + } return a; } };