TDESudo – sudo frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

450 lines
12 KiB

  1. /***************************************************************************
  2. tdesudo.cpp - description
  3. -------------------
  4. begin : Sam Feb 15 15:42:12 CET 2003
  5. copyright : (C) 2003 by Robert Gruber <rgruber@users.sourceforge.net>
  6. (C) 2007 by Martin Böhm <martin.bohm@kubuntu.org>
  7. Anthony Mercatante <tonio@kubuntu.org>
  8. Canonical Ltd (Jonathan Riddell <jriddell@ubuntu.com>)
  9. (C) 2011 by Timothy Pearson <kb9vqf@pearsoncomputing.net>
  10. ***************************************************************************/
  11. /***************************************************************************
  12. * *
  13. * This program is free software; you can redistribute it and/or modify *
  14. * it under the terms of the GNU General Public License as published by *
  15. * the Free Software Foundation; either version 2 of the License, or *
  16. * (at your option) any later version. *
  17. * *
  18. ***************************************************************************/
  19. #include "tdesudo.h"
  20. #include <tqfile.h>
  21. #include <tqdir.h>
  22. #include <tqdatastream.h>
  23. #include <tqstring.h>
  24. #include <tdecmdlineargs.h>
  25. #include <tdelocale.h>
  26. #include <tdemessagebox.h>
  27. #include <kpushbutton.h>
  28. #include <kpassdlg.h>
  29. #include <kstandarddirs.h>
  30. #include <tdesu/kcookie.h>
  31. #include <kdebug.h>
  32. #include <kshell.h>
  33. #include <tdetempfile.h>
  34. #include <iostream>
  35. #include <cstdio>
  36. #include <cstdlib>
  37. #include <signal.h>
  38. #include <sys/stat.h>
  39. #include <sys/types.h>
  40. TdeSudo::TdeSudo(TQWidget *parent, const char *name,const TQString& icon, const TQString& generic, bool withIgnoreButton)
  41. : KPasswordDialog(KPasswordDialog::Password, false, false, icon, parent, name)
  42. {
  43. TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();
  44. TQString defaultComment = i18n("<b>%1</b> needs administrative privileges. Please enter your password for verification.");
  45. p=NULL;
  46. bError=false;
  47. m_pCookie = new KCookie;
  48. // Set vars
  49. bool newDcop = args->isSet("newdcop");
  50. bool realtime = args->isSet("r");
  51. bool priority = args->isSet("p");
  52. bool showCommand = (!args->isSet("d"));
  53. bool keepCurrentHome = true;
  54. bool changeUID = true;
  55. bool noExec = false;
  56. keepPwd = (!args->isSet("n"));
  57. emptyPwd = args->isSet("s");
  58. TQString runas = args->getOption("u");
  59. TQString cmd;
  60. if (!(args->isSet("c") && !args->getOption("c").stripWhiteSpace().isEmpty()) &&
  61. !(!args->isSet("c") && args->count()) &&
  62. !args->isSet("s"))
  63. {
  64. KMessageBox::information(NULL, i18n("No command arguments supplied!\nUsage: tdesudo [-u <runas>] <command>\nTdeSudo will now exit..."));
  65. noExec = true;
  66. }
  67. p = new TDEProcess;
  68. p->clearArguments();
  69. // Parsins args
  70. /* Get the comment out of cli args */
  71. TQByteArray commentBytes = args->getOption("comment");
  72. TQTextCodec* tCodecConv = TQTextCodec::codecForLocale();
  73. TQString comment = tCodecConv->toUnicode(commentBytes, commentBytes.size());
  74. if (args->isSet("f"))
  75. {
  76. // If file is writeable, do not change uid
  77. TQString filename = TQFile::decodeName(args->getOption("f"));
  78. TQString file = filename;
  79. if (!file.isEmpty())
  80. {
  81. if (file.at(0) != '/')
  82. {
  83. TDEStandardDirs dirs;
  84. dirs.addKDEDefaults();
  85. file = dirs.findResource("config", file);
  86. if (file.isEmpty())
  87. {
  88. kdError(1206) << "Config file not found: " << file << "\n";
  89. exit(1);
  90. }
  91. }
  92. TQFileInfo fi(file);
  93. if (!fi.exists())
  94. {
  95. kdError(1206) << "File does not exist: " << file << "\n";
  96. exit(1);
  97. }
  98. if (fi.isWritable())
  99. {
  100. changeUID = false;
  101. }
  102. }
  103. }
  104. if (withIgnoreButton)
  105. {
  106. setButtonText(User1, i18n("&Ignore"));
  107. }
  108. // Apologies for the C code, taken from tdelibs/tdesu/tdesu_stub.c
  109. // KControl and other places need to use the user's existing DCOP server
  110. // For that we set DCOPSERVER. Create a file in /tmp and use iceauth to add magic cookies
  111. // from the existing server and set ICEAUTHORITY to point to the file
  112. if (!newDcop) {
  113. dcopServer = m_pCookie->dcopServer();
  114. TQCString dcopAuth = m_pCookie->dcopAuth();
  115. TQCString iceAuth = m_pCookie->iceAuth();
  116. FILE *fout;
  117. char iceauthority[200];
  118. char *host, *auth;
  119. host = tqstrdup(dcopServer.ascii());
  120. auth = tqstrdup(iceAuth);
  121. int tempfile;
  122. int oldumask = umask(077);
  123. strcpy(iceauthority, "/tmp/iceauth.XXXXXXXXXX");
  124. tempfile = mkstemp(iceauthority);
  125. umask(oldumask);
  126. if (tempfile == -1) {
  127. kdError() << "error in tdesudo mkstemp" << endl;
  128. exit(1);
  129. } else {
  130. // close(tempfile); //FIXME why does this make the connect() call later crash?
  131. }
  132. iceauthorityFile = iceauthority;
  133. //FIXME we should change owner of iceauthority file, but don't have permissions
  134. setenv("ICEAUTHORITY", iceauthorityFile.local8Bit(), 1);
  135. fout = popen("iceauth >/dev/null 2>&1", "w");
  136. if (!fout) {
  137. kdError() << "error in tdesudo running iceauth" << endl;
  138. exit(1);
  139. }
  140. fprintf(fout, "add ICE \"\" %s %s\n", host, auth);
  141. auth = tqstrdup(dcopAuth);
  142. //auth = xstrsep(params[P_DCOP_AUTH].value);
  143. fprintf(fout, "add DCOP \"\" %s %s\n", host, auth);
  144. unsetenv("ICEAUTHORITY");
  145. pclose(fout);
  146. // Exporting the user's sycoca
  147. kdeSycoca = TQFile::encodeName(locateLocal("cache", "tdesycoca"));
  148. }
  149. connect( p, TQT_SIGNAL(receivedStdout(TDEProcess*, char*, int)), this, TQT_SLOT(receivedOut(TDEProcess*, char*, int)) );
  150. connect( p, TQT_SIGNAL(receivedStderr(TDEProcess*, char*, int)), this, TQT_SLOT(receivedOut(TDEProcess*, char*, int)) );
  151. connect( p, TQT_SIGNAL(processExited (TDEProcess *)), this, TQT_SLOT(procExited(TDEProcess*)));
  152. TQString xauthenv = TQString(getenv("HOME")) + "/.Xauthority";
  153. p->setEnvironment("XAUTHORITY", xauthenv);
  154. // Generate the xauth cookie and put it in a tempfile
  155. // set the environment variables to reflect that.
  156. // Default cookie-timeout is 60 sec. .
  157. // 'man xauth' for more info on xauth cookies.
  158. KTempFile temp = KTempFile("/tmp/tdesudo-","-xauth");
  159. m_tmpname = temp.name();
  160. FILE *f;
  161. char buf[1024];
  162. TQCString disp = m_pCookie->display();
  163. // command: xauth -q -f m_tmpname generate $DISPLAy . trusted timeout 60
  164. TQString c = "/usr/bin/xauth -q -f " + m_tmpname + " generate "
  165. + TQString::fromLocal8Bit(disp) + " . trusted timeout 60";
  166. blockSigChild(); // pclose uses waitpid()
  167. if (!(f = popen(c.local8Bit(), "r"))) {
  168. kdWarning() << k_lineinfo << "Cannot run: " << c << "\n";
  169. unblockSigChild();
  170. return;
  171. }
  172. // non root users need to be able to read the xauth file.
  173. // the xauth file is deleted when tdesudo exits. security?
  174. TQFile tf(m_tmpname);
  175. if (!runas.isEmpty() && runas != "root" && tf.exists())
  176. chmod(m_tmpname.ascii(),0644);
  177. QCStringList output;
  178. while (fgets(buf, 1024, f) != NULL)
  179. output += buf;
  180. if (pclose(f) < 0) {
  181. kdError() << k_lineinfo << "Could not run xauth.\n";
  182. unblockSigChild();
  183. return;
  184. }
  185. unblockSigChild();
  186. p->setEnvironment("DISPLAY", disp);
  187. p->setEnvironment("XAUTHORITY", m_tmpname);
  188. if (emptyPwd)
  189. *p << "sudo" << "-k";
  190. else
  191. {
  192. if (changeUID)
  193. {
  194. *p << "sudo";
  195. if (!keepCurrentHome)
  196. *p << "-H";
  197. *p << "-S" << "-p" << "passprompt";
  198. if (!runas.isEmpty())
  199. *p << "-u" << runas;
  200. }
  201. if (!dcopServer.isEmpty())
  202. *p << "DCOPSERVER=" + dcopServer;
  203. if (!iceauthorityFile.isEmpty())
  204. *p << "ICEAUTHORITY=" + iceauthorityFile;
  205. if (!kdeSycoca.isEmpty())
  206. *p << "TDESYCOCA=" + kdeSycoca;
  207. if (realtime)
  208. {
  209. *p << "nice" << "-n" << "10";
  210. addLine(i18n("Priority:"), i18n("realtime:") + TQChar(' ') + TQString("50/100"));
  211. }
  212. else if (priority)
  213. {
  214. TQString n = args->getOption("p");
  215. int intn = atoi(n.ascii());
  216. intn = (intn * 40 / 100) - (20 + 0.5);
  217. TQString strn;
  218. strn.sprintf("%d",intn);
  219. *p << "nice" << "-n" << strn;
  220. addLine(i18n("Priority:"), n + TQString("/100"));
  221. }
  222. *p << "--";
  223. if (args->isSet("c"))
  224. {
  225. TQString command = args->getOption("c");
  226. TQStringList commandSplit = KShell::splitArgs(command);
  227. for (int i = 0; i < commandSplit.count(); i++)
  228. {
  229. TQString arg = validArg(commandSplit[i]);
  230. *p << arg;
  231. if (i == 0)
  232. cmd += validArg(commandSplit[i]) + TQChar(' ');
  233. else
  234. cmd += TDEProcess::quote(validArg(commandSplit[i])) + TQChar(' ');
  235. }
  236. }
  237. if (args->count())
  238. {
  239. for (int i = 0; i < args->count(); i++)
  240. {
  241. if ((!args->isSet("c")) && (i == 0))
  242. {
  243. TQStringList argsSplit = KShell::splitArgs(args->arg(i));
  244. for (int j = 0; j < argsSplit.count(); j++)
  245. {
  246. *p << validArg(argsSplit[j]);
  247. if (j == 0)
  248. cmd += validArg(argsSplit[j]) + TQChar(' ');
  249. else
  250. cmd += TDEProcess::quote(validArg(argsSplit[j])) + TQChar(' ');
  251. }
  252. }
  253. else
  254. {
  255. *p << validArg(args->arg(i));
  256. cmd += validArg(args->arg(i)) + TQChar(' ');
  257. }
  258. }
  259. }
  260. if (showCommand && !cmd.isEmpty())
  261. addLine(i18n("Command:"), cmd);
  262. }
  263. if (comment.isEmpty())
  264. {
  265. if (!generic.isEmpty())
  266. setPrompt(defaultComment.arg(generic));
  267. else
  268. setPrompt(defaultComment.arg(cmd));
  269. }
  270. else
  271. setPrompt(comment);
  272. if (noExec)
  273. exit(0);
  274. else
  275. p->start( TDEProcess::NotifyOnExit, TDEProcess::All );
  276. }
  277. TdeSudo::~TdeSudo()
  278. {
  279. }
  280. void TdeSudo::receivedOut(TDEProcess*, char*buffer, int buflen)
  281. {
  282. char *pcTmp= new char[buflen+1];
  283. strncpy(pcTmp,buffer,buflen);
  284. pcTmp[buflen]='\0';
  285. TQString strOut(pcTmp);
  286. std::cout << strOut.local8Bit() << std::endl;
  287. static int badpass = 0;
  288. if (strOut.find("Sorry, try again")!=-1)
  289. {
  290. badpass++;
  291. if (badpass>2)
  292. {
  293. bError=true;
  294. KMessageBox::error(this, i18n("Wrong password! Exiting..."));
  295. kapp->quit();
  296. }
  297. }
  298. if (strOut.find("command not found")!=-1)
  299. {
  300. bError=true;
  301. KMessageBox::error(this, i18n("Command not found!"));
  302. kapp->quit();
  303. }
  304. if (strOut.find("is not in the sudoers file")!=-1)
  305. {
  306. bError=true;
  307. KMessageBox::error(this, i18n("Your username is unknown to sudo!"));
  308. kapp->quit();
  309. }
  310. if (strOut.find("is not allowed to execute")!=-1)
  311. {
  312. bError=true;
  313. KMessageBox::error(this, i18n("Your user is not allowed to run the specified command!"));
  314. kapp->quit();
  315. }
  316. if (strOut.find("is not allowed to run sudo on")!=-1)
  317. {
  318. bError=true;
  319. KMessageBox::error(this, i18n("Your user is not allowed to run sudo on this host!"));
  320. kapp->quit();
  321. }
  322. if (strOut.find("may not run sudo on")!=-1)
  323. {
  324. bError=true;
  325. KMessageBox::error(this, i18n("Your user is not allowed to run sudo on this host!"));
  326. kapp->quit();
  327. }
  328. if ((strOut.find("passprompt")!=-1) || (strOut.find("PIN (CHV2)")!=-1))
  329. {
  330. this->clearPassword();
  331. this->show();
  332. }
  333. }
  334. void TdeSudo::procExited(TDEProcess*)
  335. {
  336. if (!keepPwd && unCleaned)
  337. {
  338. unCleaned = false;
  339. p->clearArguments();
  340. *p << "sudo" << "-k";
  341. p->start( TDEProcess::NotifyOnExit, TDEProcess::All );
  342. }
  343. if (!newDcop && !iceauthorityFile.isEmpty())
  344. if (!iceauthorityFile.isEmpty())
  345. TQFile::remove(iceauthorityFile);
  346. if (!bError) {
  347. if (!m_tmpname.isEmpty())
  348. TQFile::remove(m_tmpname);
  349. kapp->quit();
  350. }
  351. }
  352. void TdeSudo::slotOk()
  353. {
  354. TQString strTmp(password());
  355. strTmp+="\n";
  356. p->writeStdin(strTmp.ascii(),(int)strTmp.length());
  357. this->hide();
  358. }
  359. void TdeSudo::slotUser1()
  360. {
  361. done(AsUser);
  362. }
  363. void TdeSudo::blockSigChild()
  364. {
  365. sigset_t sset;
  366. sigemptyset(&sset);
  367. sigaddset(&sset, SIGCHLD);
  368. sigprocmask(SIG_BLOCK, &sset, 0L);
  369. }
  370. void TdeSudo::unblockSigChild()
  371. {
  372. sigset_t sset;
  373. sigemptyset(&sset);
  374. sigaddset(&sset, SIGCHLD);
  375. sigprocmask(SIG_UNBLOCK, &sset, 0L);
  376. }
  377. TQString TdeSudo::validArg(TQString arg)
  378. {
  379. TQChar firstChar = arg.at(0);
  380. TQChar lastChar = arg.at(arg.length() - 1);
  381. if ( (firstChar == '"' && lastChar == '"') || (firstChar == '\'' && lastChar == '\'') )
  382. {
  383. arg = arg.remove(0, 1);
  384. arg = arg.remove(arg.length() - 1, 1);
  385. }
  386. return arg;
  387. }
  388. #include "tdesudo.moc"