/*************************************************************************** tdesudo.cpp - description ------------------- begin : Sam Feb 15 15:42:12 CET 2003 copyright : (C) 2003 by Robert Gruber (C) 2007 by Martin Böhm Anthony Mercatante Canonical Ltd (Jonathan Riddell ) (C) 2011 by Timothy Pearson ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "tdesudo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include TdeSudo::TdeSudo(TQWidget *parent, const char *name,const TQString& icon, const TQString& generic, bool withIgnoreButton) : KPasswordDialog(KPasswordDialog::Password, false, false, icon, parent, name) { TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs(); TQString defaultComment = i18n("%1 needs administrative privileges. Please enter your password for verification."); p=NULL; bError=false; m_pCookie = new KCookie; // Set vars bool newDcop = args->isSet("newdcop"); bool realtime = args->isSet("r"); bool priority = args->isSet("p"); bool showCommand = (!args->isSet("d")); bool keepCurrentHome = true; bool changeUID = true; bool noExec = false; keepPwd = (!args->isSet("n")); emptyPwd = args->isSet("s"); TQString runas = args->getOption("u"); TQString cmd; if (!(args->isSet("c") && !args->getOption("c").stripWhiteSpace().isEmpty()) && !(!args->isSet("c") && args->count()) && !args->isSet("s")) { KMessageBox::information(NULL, i18n("No command arguments supplied!\nUsage: tdesudo [-u ] \nTdeSudo will now exit...")); noExec = true; } p = new TDEProcess; p->clearArguments(); // Parsins args /* Get the comment out of cli args */ TQByteArray commentBytes = args->getOption("comment"); TQTextCodec* tCodecConv = TQTextCodec::codecForLocale(); TQString comment = tCodecConv->toUnicode(commentBytes, commentBytes.size()); if (args->isSet("f")) { // If file is writeable, do not change uid TQString filename = TQFile::decodeName(args->getOption("f")); TQString file = filename; if (!file.isEmpty()) { if (file.at(0) != '/') { TDEStandardDirs dirs; dirs.addKDEDefaults(); file = dirs.findResource("config", file); if (file.isEmpty()) { kdError(1206) << "Config file not found: " << file << "\n"; exit(1); } } TQFileInfo fi(file); if (!fi.exists()) { kdError(1206) << "File does not exist: " << file << "\n"; exit(1); } if (fi.isWritable()) { changeUID = false; } } } if (withIgnoreButton) { setButtonText(User1, i18n("&Ignore")); } // Apologies for the C code, taken from tdelibs/tdesu/tdesu_stub.c // KControl and other places need to use the user's existing DCOP server // For that we set DCOPSERVER. Create a file in /tmp and use iceauth to add magic cookies // from the existing server and set ICEAUTHORITY to point to the file if (!newDcop) { dcopServer = m_pCookie->dcopServer(); TQCString dcopAuth = m_pCookie->dcopAuth(); TQCString iceAuth = m_pCookie->iceAuth(); FILE *fout; char iceauthority[200]; char *host, *auth; host = tqstrdup(dcopServer); auth = tqstrdup(iceAuth); int tempfile; int oldumask = umask(077); strcpy(iceauthority, "/tmp/iceauth.XXXXXXXXXX"); tempfile = mkstemp(iceauthority); umask(oldumask); if (tempfile == -1) { kdError() << "error in tdesudo mkstemp" << endl; exit(1); } else { // close(tempfile); //FIXME why does this make the connect() call later crash? } iceauthorityFile = iceauthority; //FIXME we should change owner of iceauthority file, but don't have permissions setenv("ICEAUTHORITY", iceauthorityFile, 1); fout = popen("iceauth >/dev/null 2>&1", "w"); if (!fout) { kdError() << "error in tdesudo running iceauth" << endl; exit(1); } fprintf(fout, "add ICE \"\" %s %s\n", host, auth); auth = tqstrdup(dcopAuth); //auth = xstrsep(params[P_DCOP_AUTH].value); fprintf(fout, "add DCOP \"\" %s %s\n", host, auth); unsetenv("ICEAUTHORITY"); pclose(fout); // Exporting the user's sycoca kdeSycoca = TQFile::encodeName(locateLocal("cache", "tdesycoca")); } connect( p, TQT_SIGNAL(receivedStdout(TDEProcess*, char*, int)), this, TQT_SLOT(receivedOut(TDEProcess*, char*, int)) ); connect( p, TQT_SIGNAL(receivedStderr(TDEProcess*, char*, int)), this, TQT_SLOT(receivedOut(TDEProcess*, char*, int)) ); connect( p, TQT_SIGNAL(processExited (TDEProcess *)), this, TQT_SLOT(procExited(TDEProcess*))); TQString xauthenv = TQString(getenv("HOME")) + "/.Xauthority"; p->setEnvironment("XAUTHORITY", xauthenv); // Generate the xauth cookie and put it in a tempfile // set the environment variables to reflect that. // Default cookie-timeout is 60 sec. . // 'man xauth' for more info on xauth cookies. KTempFile temp = KTempFile("/tmp/tdesudo-","-xauth"); m_tmpname = temp.name(); FILE *f; char buf[1024]; TQCString disp = m_pCookie->display(); // command: xauth -q -f m_tmpname generate $DISPLAy . trusted timeout 60 TQString c = "/usr/bin/xauth -q -f " + m_tmpname + " generate " + TQString::fromLocal8Bit(disp) + " . trusted timeout 60"; blockSigChild(); // pclose uses waitpid() if (!(f = popen(c, "r"))) { kdWarning() << k_lineinfo << "Cannot run: " << c << "\n"; unblockSigChild(); return; } // non root users need to be able to read the xauth file. // the xauth file is deleted when tdesudo exits. security? TQFile tf(m_tmpname); if (!runas.isEmpty() && runas != "root" && tf.exists()) chmod(m_tmpname.ascii(),0644); QCStringList output; while (fgets(buf, 1024, f) > 0) output += buf; if (pclose(f) < 0) { kdError() << k_lineinfo << "Could not run xauth.\n"; unblockSigChild(); return; } unblockSigChild(); p->setEnvironment("DISPLAY", disp); p->setEnvironment("XAUTHORITY", m_tmpname); if (emptyPwd) *p << "sudo" << "-k"; else { if (changeUID) { *p << "sudo"; if (!keepCurrentHome) *p << "-H"; *p << "-S" << "-p" << "passprompt"; if (!runas.isEmpty()) *p << "-u" << runas; } if (!dcopServer.isEmpty()) *p << "DCOPSERVER=" + dcopServer; if (!iceauthorityFile.isEmpty()) *p << "ICEAUTHORITY=" + iceauthorityFile; if (!kdeSycoca.isEmpty()) *p << "TDESYCOCA=" + kdeSycoca; if (realtime) { *p << "nice" << "-n" << "10"; addLine(i18n("Priority:"), i18n("realtime:") + TQChar(' ') + TQString("50/100")); } else if (priority) { TQString n = args->getOption("p"); int intn = atoi(n); intn = (intn * 40 / 100) - (20 + 0.5); TQString strn; strn.sprintf("%d",intn); *p << "nice" << "-n" << strn; addLine(i18n("Priority:"), n + TQString("/100")); } *p << "--"; if (args->isSet("c")) { TQString command = args->getOption("c"); TQStringList commandSplit = KShell::splitArgs(command); for (int i = 0; i < commandSplit.count(); i++) { TQString arg = validArg(commandSplit[i]); *p << arg; if (i == 0) cmd += validArg(commandSplit[i]) + TQChar(' '); else cmd += TDEProcess::quote(validArg(commandSplit[i])) + TQChar(' '); } } if (args->count()) { for (int i = 0; i < args->count(); i++) { if ((!args->isSet("c")) && (i == 0)) { TQStringList argsSplit = KShell::splitArgs(args->arg(i)); for (int j = 0; j < argsSplit.count(); j++) { *p << validArg(argsSplit[j]); if (j == 0) cmd += validArg(argsSplit[j]) + TQChar(' '); else cmd += TDEProcess::quote(validArg(argsSplit[j])) + TQChar(' '); } } else { *p << validArg(args->arg(i)); cmd += validArg(args->arg(i)) + TQChar(' '); } } } if (showCommand && !cmd.isEmpty()) addLine(i18n("Command:"), cmd); } if (comment.isEmpty()) { if (!generic.isEmpty()) setPrompt(defaultComment.arg(generic)); else setPrompt(defaultComment.arg(cmd)); } else setPrompt(comment); if (noExec) exit(0); else p->start( TDEProcess::NotifyOnExit, TDEProcess::All ); } TdeSudo::~TdeSudo() { } void TdeSudo::receivedOut(TDEProcess*, char*buffer, int buflen) { char *pcTmp= new char[buflen+1]; strncpy(pcTmp,buffer,buflen); pcTmp[buflen]='\0'; TQString strOut(pcTmp); std::cout << strOut << std::endl; static int badpass = 0; if (strOut.find("Sorry, try again")!=-1) { badpass++; if (badpass>2) { bError=true; KMessageBox::error(this, i18n("Wrong password! Exiting...")); kapp->quit(); } } if (strOut.find("command not found")!=-1) { bError=true; KMessageBox::error(this, i18n("Command not found!")); kapp->quit(); } if (strOut.find("is not in the sudoers file")!=-1) { bError=true; KMessageBox::error(this, i18n("Your username is unknown to sudo!")); kapp->quit(); } if (strOut.find("is not allowed to execute")!=-1) { bError=true; KMessageBox::error(this, i18n("Your user is not allowed to run the specified command!")); kapp->quit(); } if (strOut.find("is not allowed to run sudo on")!=-1) { bError=true; KMessageBox::error(this, i18n("Your user is not allowed to run sudo on this host!")); kapp->quit(); } if (strOut.find("may not run sudo on")!=-1) { bError=true; KMessageBox::error(this, i18n("Your user is not allowed to run sudo on this host!")); kapp->quit(); } if ((strOut.find("passprompt")!=-1) || (strOut.find("PIN (CHV2)")!=-1)) { this->clearPassword(); this->show(); } } void TdeSudo::procExited(TDEProcess*) { if (!keepPwd && unCleaned) { unCleaned = false; p->clearArguments(); *p << "sudo" << "-k"; p->start( TDEProcess::NotifyOnExit, TDEProcess::All ); } if (!newDcop && !iceauthorityFile.isEmpty()) if (!iceauthorityFile.isEmpty()) TQFile::remove(iceauthorityFile); if (!bError) { if (!m_tmpname.isEmpty()) TQFile::remove(m_tmpname); kapp->quit(); } } void TdeSudo::slotOk() { TQString strTmp(password()); strTmp+="\n"; p->writeStdin(strTmp.ascii(),(int)strTmp.length()); this->hide(); } void TdeSudo::slotUser1() { done(AsUser); } void TdeSudo::blockSigChild() { sigset_t sset; sigemptyset(&sset); sigaddset(&sset, SIGCHLD); sigprocmask(SIG_BLOCK, &sset, 0L); } void TdeSudo::unblockSigChild() { sigset_t sset; sigemptyset(&sset); sigaddset(&sset, SIGCHLD); sigprocmask(SIG_UNBLOCK, &sset, 0L); } TQString TdeSudo::validArg(TQString arg) { TQChar firstChar = arg.at(0); TQChar lastChar = arg.at(arg.length() - 1); if ( (firstChar == '"' && lastChar == '"') || (firstChar == '\'' && lastChar == '\'') ) { arg = arg.remove(0, 1); arg = arg.remove(arg.length() - 1, 1); } return arg; }