static int startApp() { KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); // Stop daemon and exit? if (args->isSet("s")) { KDEsuClient client; if (client.ping() == -1) { kdError(1206) << "Daemon not running -- nothing to stop\n"; exit(1); } if (client.stopServer() != -1) { kdDebug(1206) << "Daemon stopped\n"; exit(0); } kdError(1206) << "Could not stop daemon\n"; exit(1); } QString icon; if ( args->isSet("i")) icon = args->getOption("i"); bool prompt = true; if ( args->isSet("d")) prompt = false; // Get target uid QCString user = args->getOption("u"); QCString auth_user = user; struct passwd *pw = getpwnam(user); if (pw == 0L) { kdError(1206) << "User " << user << " does not exist\n"; exit(1); } bool change_uid = (getuid() != pw->pw_uid); // If file is writeable, do not change uid QString file = QFile::decodeName(args->getOption("f")); if (change_uid && !file.isEmpty()) { if (file.at(0) != '/') { KStandardDirs dirs; dirs.addKDEDefaults(); file = dirs.findResource("config", file); if (file.isEmpty()) { kdError(1206) << "Config file not found: " << file << "\n"; exit(1); } } QFileInfo fi(file); if (!fi.exists()) { kdError(1206) << "File does not exist: " << file << "\n"; exit(1); } change_uid = !fi.isWritable(); } // Get priority/scheduler QCString tmp = args->getOption("p"); bool ok; int priority = tmp.toInt(&ok); if (!ok || (priority < 0) || (priority > 100)) { KCmdLineArgs::usage(i18n("Illegal priority: %1").arg(tmp)); exit(1); } int scheduler = SuProcess::SchedNormal; if (args->isSet("r")) scheduler = SuProcess::SchedRealtime; if ((priority > 50) || (scheduler != SuProcess::SchedNormal)) { change_uid = true; auth_user = "******"; } // Get command if (args->isSet("c")) { command = args->getOption("c"); for (int i=0; i<args->count(); i++) { QString arg = QFile::decodeName(args->arg(i)); KRun::shellQuote(arg); command += " "; command += QFile::encodeName(arg); } } else { if( args->count() == 0 ) { KCmdLineArgs::usage(i18n("No command specified.")); exit(1); } command = args->arg(0); for (int i=1; i<args->count(); i++) { QString arg = QFile::decodeName(args->arg(i)); KRun::shellQuote(arg); command += " "; command += QFile::encodeName(arg); } } // Don't change uid if we're don't need to. if (!change_uid) { int result = system(command); result = WEXITSTATUS(result); return result; } // Check for daemon and start if necessary bool just_started = false; bool have_daemon = true; KDEsuClient client; if (!client.isServerSGID()) { kdWarning(1206) << "Daemon not safe (not sgid), not using it.\n"; have_daemon = false; } else if (client.ping() == -1) { if (client.startServer() == -1) { kdWarning(1206) << "Could not start daemon, reduced functionality.\n"; have_daemon = false; } just_started = true; } // Try to exec the command with kdesud. bool keep = !args->isSet("n") && have_daemon; bool terminal = args->isSet("t"); bool new_dcop = args->isSet("newdcop"); bool withIgnoreButton = args->isSet("ignorebutton"); QCStringList env; QCString options; env << ( "DESKTOP_STARTUP_ID=" + kapp->startupId()); if (pw->pw_uid) { // Only propagate KDEHOME for non-root users, // root uses KDEROOTHOME // Translate the KDEHOME of this user to the new user. QString kdeHome = KGlobal::dirs()->relativeLocation("home", KGlobal::dirs()->localkdedir()); if (kdeHome[0] != '/') kdeHome.prepend("~/"); else kdeHome=QString::null; // Use default env << ("KDEHOME="+ QFile::encodeName(kdeHome)); } KUser u; env << (QCString) ("KDESU_USER="******"KDESYCOCA="+QFile::encodeName(locateLocal("cache", "ksycoca")); env << ksycoca; options += "xf"; // X-only, dcop forwarding enabled. } if (keep && !terminal && !just_started) { client.setPriority(priority); client.setScheduler(scheduler); int result = client.exec(command, user, options, env); if (result == 0) { result = client.exitCode(); return result; } } // Set core dump size to 0 because we will have // root's password in memory. struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &rlim)) { kdError(1206) << "rlimit(): " << ERR << "\n"; exit(1); } // Read configuration KConfig *config = KGlobal::config(); config->setGroup("Passwords"); int timeout = config->readNumEntry("Timeout", defTimeout); // Check if we need a password SuProcess proc; proc.setUser(auth_user); int needpw = proc.checkNeedPassword(); if (needpw < 0) { QString err = i18n("Su returned with an error.\n"); KMessageBox::error(0L, err); exit(1); } if (needpw == 0) { keep = 0; kdDebug() << "Don't need password!!\n"; } // Start the dialog QCString password; if (needpw) { KStartupInfoId id; id.initId( kapp->startupId()); KStartupInfoData data; data.setSilent( KStartupInfoData::Yes ); KStartupInfo::sendChange( id, data ); KDEsuDialog dlg(user, auth_user, keep && !terminal,icon, withIgnoreButton); if (prompt) dlg.addLine(i18n("Command:"), command); if ((priority != 50) || (scheduler != SuProcess::SchedNormal)) { QString prio; if (scheduler == SuProcess::SchedRealtime) prio += i18n("realtime: "); prio += QString("%1/100").arg(priority); if (prompt) dlg.addLine(i18n("Priority:"), prio); } int ret = dlg.exec(); if (ret == KDEsuDialog::Rejected) { KStartupInfo::sendFinish( id ); exit(0); } if (ret == KDEsuDialog::AsUser) change_uid = false; password = dlg.password(); keep = dlg.keep(); data.setSilent( KStartupInfoData::No ); KStartupInfo::sendChange( id, data ); } // Some events may need to be handled (like a button animation) kapp->processEvents(); // Run command if (!change_uid) { int result = system(command); result = WEXITSTATUS(result); return result; } else if (keep && have_daemon) { client.setPass(password, timeout); client.setPriority(priority); client.setScheduler(scheduler); int result = client.exec(command, user, options, env); if (result == 0) { result = client.exitCode(); return result; } } else { SuProcess proc; proc.setTerminal(terminal); proc.setErase(true); proc.setUser(user); if (!new_dcop) { proc.setXOnly(true); proc.setDCOPForwarding(true); } proc.setEnvironment(env); proc.setPriority(priority); proc.setScheduler(scheduler); proc.setCommand(command); int result = proc.exec(password); return result; } return -1; }
int create_socket() { int sockfd; ksocklen_t addrlen; struct stat s; QCString display(getenv("DISPLAY")); if (display.isEmpty()) { kdWarning(1205) << "$DISPLAY is not set\n"; return -1; } // strip the screen number from the display display.replace(QRegExp("\\.[0-9]+$"), ""); sock = QFile::encodeName(locateLocal("socket", QString("kdesud_%1").arg(display))); int stat_err=lstat(sock, &s); if(!stat_err && S_ISLNK(s.st_mode)) { kdWarning(1205) << "Someone is running a symlink attack on you\n"; if(unlink(sock)) { kdWarning(1205) << "Could not delete symlink\n"; return -1; } } if (!access(sock, R_OK|W_OK)) { KDEsuClient client; if (client.ping() == -1) { kdWarning(1205) << "stale socket exists\n"; if (unlink(sock)) { kdWarning(1205) << "Could not delete stale socket\n"; return -1; } } else { kdWarning(1205) << "kdesud is already running\n"; return -1; } } sockfd = socket(PF_UNIX, SOCK_STREAM, 0); if (sockfd < 0) { kdError(1205) << "socket(): " << ERR << "\n"; return -1; } struct sockaddr_un addr; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1); addr.sun_path[sizeof(addr.sun_path)-1] = '\000'; addrlen = SUN_LEN(&addr); if (bind(sockfd, (struct sockaddr *)&addr, addrlen) < 0) { kdError(1205) << "bind(): " << ERR << "\n"; return -1; } struct linger lin; lin.l_onoff = lin.l_linger = 0; if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &lin, sizeof(linger)) < 0) { kdError(1205) << "setsockopt(SO_LINGER): " << ERR << "\n"; return -1; } int opt = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) { kdError(1205) << "setsockopt(SO_REUSEADDR): " << ERR << "\n"; return -1; } opt = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof(opt)) < 0) { kdError(1205) << "setsockopt(SO_KEEPALIVE): " << ERR << "\n"; return -1; } chmod(sock, 0600); return sockfd; }
static int startApp() { KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); // Stop daemon and exit? if (args->isSet("s")) { KDEsuClient client; if (client.ping() == -1) { kError(1206) << "Daemon not running -- nothing to stop\n"; exit(1); } if (client.stopServer() != -1) { kDebug(1206) << "Daemon stopped\n"; exit(0); } kError(1206) << "Could not stop daemon\n"; exit(1); } QString icon; if ( args->isSet("i")) icon = args->getOption("i"); bool prompt = true; if ( args->isSet("d")) prompt = false; // Get target uid QByteArray user = args->getOption("u").toLocal8Bit(); QByteArray auth_user = user; struct passwd *pw = getpwnam(user); if (pw == 0L) { kError(1206) << "User " << user << " does not exist\n"; exit(1); } bool other_uid = (getuid() != pw->pw_uid); bool change_uid = other_uid; if (!change_uid) { char *cur_user = getenv("USER"); if (!cur_user) cur_user = getenv("LOGNAME"); change_uid = (!cur_user || user != cur_user); } // If file is writeable, do not change uid QString file = args->getOption("f"); if (other_uid && !file.isEmpty()) { if (file.at(0) != '/') { KStandardDirs dirs; file = dirs.findResource("config", file); if (file.isEmpty()) { kError(1206) << "Config file not found: " << file << "\n"; exit(1); } } QFileInfo fi(file); if (!fi.exists()) { kError(1206) << "File does not exist: " << file << "\n"; exit(1); } change_uid = !fi.isWritable(); } // Get priority/scheduler QString tmp = args->getOption("p"); bool ok; int priority = tmp.toInt(&ok); if (!ok || (priority < 0) || (priority > 100)) { KCmdLineArgs::usageError(i18n("Illegal priority: %1", tmp)); exit(1); } int scheduler = SuProcess::SchedNormal; if (args->isSet("r")) scheduler = SuProcess::SchedRealtime; if ((priority > 50) || (scheduler != SuProcess::SchedNormal)) { change_uid = true; auth_user = "******"; } // Get command if (args->isSet("c")) { command = args->getOption("c").toLocal8Bit(); // Accepting additional arguments here is somewhat weird, // but one can conceive use cases: have a complex command with // redirections and additional file names which need to be quoted // safely. } else { if( args->count() == 0 ) { KCmdLineArgs::usageError(i18n("No command specified.")); exit(1); } } for (int i = 0; i < args->count(); i++) { command += ' '; command += QFile::encodeName(KShell::quoteArg(args->arg(i))); } // Don't change uid if we're don't need to. if (!change_uid) { int result = system(command); result = WEXITSTATUS(result); return result; } // Check for daemon and start if necessary bool just_started = false; bool have_daemon = true; KDEsuClient client; if (!client.isServerSGID()) { kWarning(1206) << "Daemon not safe (not sgid), not using it.\n"; have_daemon = false; } else if (client.ping() == -1) { if (client.startServer() == -1) { kWarning(1206) << "Could not start daemon, reduced functionality.\n"; have_daemon = false; } just_started = true; } // Try to exec the command with kdesud. bool keep = !args->isSet("n") && have_daemon; bool terminal = args->isSet("t"); bool withIgnoreButton = args->isSet("ignorebutton"); int winid = -1; bool attach = args->isSet("attach"); if(attach) { winid = args->getOption("attach").toInt(&attach, 0); //C style parsing. If the string begins with "0x", base 16 is used; if the string begins with "0", base 8 is used; otherwise, base 10 is used. if(!attach) kWarning(1206) << "Specified winid to attach to is not a valid number"; } else if(args->isSet("embed")) { /* KDialog originally used --embed for attaching the dialog box. However this is misleading and so we changed to --attach. * For consistancy, we silently map --embed to --attach */ attach = true; winid = args->getOption("embed").toInt(&attach, 0); //C style parsing. If the string begins with "0x", base 16 is used; if the string begins with "0", base 8 is used; otherwise, base 10 is used. if(!attach) kWarning(1206) << "Specified winid to attach to is not a valid number"; } QList<QByteArray> env; QByteArray options; env << ( "DESKTOP_STARTUP_ID=" + kapp->startupId()); if (pw->pw_uid) { // Only propagate KDEHOME for non-root users, // root uses KDEROOTHOME // Translate the KDEHOME of this user to the new user. QString kdeHome = KGlobal::dirs()->relativeLocation("home", KGlobal::dirs()->localkdedir()); if (kdeHome[0] != '/') kdeHome.prepend("~/"); else kdeHome.clear(); // Use default env << ("KDEHOME="+ QFile::encodeName(kdeHome)); } KUser u; env << (QByteArray) ("KDESU_USER="******"rlimit(): " << ERR << "\n"; exit(1); } // Read configuration KConfigGroup config(KGlobal::config(), "Passwords"); int timeout = config.readEntry("Timeout", defTimeout); // Check if we need a password SuProcess proc; proc.setUser(auth_user); int needpw = proc.checkNeedPassword(); if (needpw < 0) { QString err = i18n("Su returned with an error.\n"); KMessageBox::error(0L, err); exit(1); } if (needpw == 0) { keep = 0; kDebug() << "Don't need password!!\n"; } // Start the dialog QString password; if (needpw) { #ifdef Q_WS_X11 KStartupInfoId id; id.initId( kapp->startupId()); KStartupInfoData data; data.setSilent( KStartupInfoData::Yes ); KStartupInfo::sendChange( id, data ); #endif KDEsuDialog dlg(user, auth_user, keep && !terminal, icon, withIgnoreButton); if (prompt) dlg.addCommentLine(i18n("Command:"), QFile::decodeName(command)); if (defKeep) dlg.setKeepPassword(true); if ((priority != 50) || (scheduler != SuProcess::SchedNormal)) { QString prio; if (scheduler == SuProcess::SchedRealtime) prio += i18n("realtime: "); prio += QString("%1/100").arg(priority); if (prompt) dlg.addCommentLine(i18n("Priority:"), prio); } //Attach dialog #ifdef Q_WS_X11 if(attach) KWindowSystem::setMainWindow(&dlg, (WId)winid); #endif int ret = dlg.exec(); if (ret == KDEsuDialog::Rejected) { #ifdef Q_WS_X11 KStartupInfo::sendFinish( id ); #endif exit(1); } if (ret == KDEsuDialog::AsUser) change_uid = false; password = dlg.password(); keep = dlg.keepPassword(); #ifdef Q_WS_X11 data.setSilent( KStartupInfoData::No ); KStartupInfo::sendChange( id, data ); #endif } // Some events may need to be handled (like a button animation) kapp->processEvents(); // Run command if (!change_uid) { int result = system(command); result = WEXITSTATUS(result); return result; } else if (keep && have_daemon) { client.setPass(password.toLocal8Bit(), timeout); client.setPriority(priority); client.setScheduler(scheduler); int result = client.exec(command, user, options, env); if (result == 0) { result = client.exitCode(); return result; } } else { SuProcess proc; proc.setTerminal(terminal); proc.setErase(true); proc.setUser(user); proc.setEnvironment(env); proc.setPriority(priority); proc.setScheduler(scheduler); proc.setCommand(command); int result = proc.exec(password.toLocal8Bit()); return result; } return -1; }