QtPass  1.2.3
Multi-platform GUI for pass, the standard unix password manager.
pass.cpp
Go to the documentation of this file.
1 #include "pass.h"
2 
3 #include <utility>
4 #include "qtpasssettings.h"
5 #include "util.h"
6 
7 #ifdef QT_DEBUG
8 #include "debughelper.h"
9 #endif
10 
11 using namespace std;
12 using namespace Enums;
13 
17 Pass::Pass() : wrapperRunning(false), env(QProcess::systemEnvironment()) {
18  connect(&exec,
19  static_cast<void (Executor::*)(int, int, const QString &,
20  const QString &)>(&Executor::finished),
21  this, &Pass::finished);
22 
23  // TODO(bezet): stop using process
24  // connect(&process, SIGNAL(error(QProcess::ProcessError)), this,
25  // SIGNAL(error(QProcess::ProcessError)));
26 
28 }
29 
30 void Pass::executeWrapper(PROCESS id, const QString &app,
31  const QStringList &args, bool readStdout,
32  bool readStderr) {
33  executeWrapper(id, app, args, QString(), readStdout, readStderr);
34 }
35 
36 void Pass::executeWrapper(PROCESS id, const QString &app,
37  const QStringList &args, QString input,
38  bool readStdout, bool readStderr) {
39 #ifdef QT_DEBUG
40  dbg() << app << args;
41 #endif
42  exec.execute(id, QtPassSettings::getPassStore(), app, args, std::move(input), readStdout,
43  readStderr);
44 }
45 
46 void Pass::init() {
47 #ifdef __APPLE__
48  // If it exists, add the gpgtools to PATH
49  if (QFile("/usr/local/MacGPG2/bin").exists())
50  env.replaceInStrings("PATH=", "PATH=/usr/local/MacGPG2/bin:");
51  // Add missing /usr/local/bin
52  if (env.filter("/usr/local/bin").isEmpty())
53  env.replaceInStrings("PATH=", "PATH=/usr/local/bin:");
54 #endif
55 
56  if (!QtPassSettings::getGpgHome().isEmpty()) {
57  QDir absHome(QtPassSettings::getGpgHome());
58  absHome.makeAbsolute();
59  env << "GNUPGHOME=" + absHome.path();
60  }
61 }
62 
70 QString Pass::Generate_b(unsigned int length, const QString &charset) {
71  QString passwd;
73  // --secure goes first as it overrides --no-* otherwise
74  QStringList args;
75  args.append("-1");
77  args.append("--secure");
78  args.append(QtPassSettings::isAvoidCapitals() ? "--no-capitalize"
79  : "--capitalize");
80  args.append(QtPassSettings::isAvoidNumbers() ? "--no-numerals"
81  : "--numerals");
83  args.append("--symbols");
84  args.append(QString::number(length));
85  QString p_out;
86  // TODO(bezet): try-catch here(2 statuses to merge o_O)
88  &passwd) == 0)
89  passwd.remove(QRegExp("[\\n\\r]"));
90  else {
91  passwd.clear();
92 #ifdef QT_DEBUG
93  qDebug() << __FILE__ << ":" << __LINE__ << "\t"
94  << "pwgen fail";
95 #endif
96  // TODO(bezet): emit critical ?
97  }
98  } else {
99  if (charset.length() > 0) {
100  passwd = generateRandomPassword(charset, length);
101  } else {
102  emit critical(
103  tr("No characters chosen"),
104  tr("Can't generate password, there are no characters to choose from "
105  "set in the configuration!"));
106  }
107  }
108  return passwd;
109 }
110 
115 void Pass::GenerateGPGKeys(QString batch) {
117  {"--gen-key", "--no-tty", "--batch"}, std::move(batch));
118  // TODO check status / error messages - probably not here, it's just started
119  // here, see finished for details
120  // https://github.com/IJHack/QtPass/issues/202#issuecomment-251081688
121 }
122 
129 QList<UserInfo> Pass::listKeys(QStringList keystrings, bool secret) {
130  QList<UserInfo> users;
131  QStringList args = {"--no-tty", "--with-colons"};
132  args.append(secret ? "--list-secret-keys" : "--list-keys");
133 
134  foreach (QString keystring, keystrings) {
135  if (!keystring.isEmpty()) {
136  args.append(keystring);
137  }
138  }
139  QString p_out;
141  0)
142  return users;
143  QStringList keys = p_out.split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
144  UserInfo current_user;
145  foreach (QString key, keys) {
146  QStringList props = key.split(':');
147  if (props.size() < 10)
148  continue;
149  if (props[0] == (secret ? "sec" : "pub")) {
150  if (!current_user.key_id.isEmpty())
151  users.append(current_user);
152  current_user = UserInfo();
153  current_user.key_id = props[4];
154  current_user.name = props[9].toUtf8();
155  current_user.validity = props[1][0].toLatin1();
156  current_user.created.setTime_t(props[5].toUInt());
157  current_user.expiry.setTime_t(props[6].toUInt());
158  } else if (current_user.name.isEmpty() && props[0] == "uid") {
159  current_user.name = props[9];
160  }
161  }
162  if (!current_user.key_id.isEmpty())
163  users.append(current_user);
164  return users;
165 }
166 
173 QList<UserInfo> Pass::listKeys(const QString& keystring, bool secret) {
174  return listKeys(QStringList(keystring), secret);
175 }
176 
187 void Pass::finished(int id, int exitCode, const QString &out,
188  const QString &err) {
189  auto pid = static_cast<PROCESS>(id);
190  if (exitCode != 0) {
191  emit processErrorExit(exitCode, err);
192  return;
193  }
194  switch (pid) {
195  case GIT_INIT:
196  emit finishedGitInit(out, err);
197  break;
198  case GIT_PULL:
199  emit finishedGitPull(out, err);
200  break;
201  case GIT_PUSH:
202  emit finishedGitPush(out, err);
203  break;
204  case PASS_SHOW:
205  emit finishedShow(out);
206  break;
207  case PASS_OTP_GENERATE:
208  emit finishedOtpGenerate(out);
209  break;
210  case PASS_INSERT:
211  emit finishedInsert(out, err);
212  break;
213  case PASS_REMOVE:
214  emit finishedRemove(out, err);
215  break;
216  case PASS_INIT:
217  emit finishedInit(out, err);
218  break;
219  case PASS_MOVE:
220  emit finishedMove(out, err);
221  break;
222  case PASS_COPY:
223  emit finishedCopy(out, err);
224  break;
225  default:
226 #ifdef QT_DEBUG
227  dbg() << "Unhandled process type" << pid;
228 #endif
229  break;
230  }
231 }
232 
238  QStringList store = env.filter("PASSWORD_STORE_DIR");
239  // put PASSWORD_STORE_DIR in env
240  if (store.isEmpty()) {
241  // dbg()<< "Added
242  // PASSWORD_STORE_DIR";
243  env.append("PASSWORD_STORE_DIR=" + QtPassSettings::getPassStore());
244  } else {
245  // dbg()<< "Update
246  // PASSWORD_STORE_DIR with " + passStore;
247  env.replaceInStrings(store.first(), "PASSWORD_STORE_DIR=" +
249  }
250  exec.setEnvironment(env);
251 }
252 
258 QStringList Pass::getRecipientList(const QString& for_file) {
259  QDir gpgIdPath(QFileInfo(for_file.startsWith(QtPassSettings::getPassStore())
260  ? for_file
261  : QtPassSettings::getPassStore() + for_file)
262  .absoluteDir());
263  bool found = false;
264  while (gpgIdPath.exists() &&
265  gpgIdPath.absolutePath().startsWith(QtPassSettings::getPassStore())) {
266  if (QFile(gpgIdPath.absoluteFilePath(".gpg-id")).exists()) {
267  found = true;
268  break;
269  }
270  if (!gpgIdPath.cdUp())
271  break;
272  }
273  QFile gpgId(found ? gpgIdPath.absoluteFilePath(".gpg-id")
274  : QtPassSettings::getPassStore() + ".gpg-id");
275  if (!gpgId.open(QIODevice::ReadOnly | QIODevice::Text))
276  return QStringList();
277  QStringList recipients;
278  while (!gpgId.atEnd()) {
279  QString recipient(gpgId.readLine());
280  recipient = recipient.trimmed();
281  if (!recipient.isEmpty())
282  recipients += recipient;
283  }
284  return recipients;
285 }
286 
294 QStringList Pass::getRecipientString(QString for_file, QString separator,
295  const int *count) {
296  Q_UNUSED(separator)
297  Q_UNUSED(count)
298  return Pass::getRecipientList(std::move(for_file));
299 }
300 
301 /* Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
302  */
303 
304 quint32 Pass::boundedRandom(quint32 bound) {
305  if (bound < 2) {
306  return 0;
307  }
308 
309  quint32 randval;
310  const quint32 max_mod_bound = (1 + ~bound) % bound;
311 
312 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
313  static int fd = -1;
314  if (fd == -1) {
315  assert((fd = open("/dev/urandom", O_RDONLY)) >= 0);
316  }
317 #endif
318 
319  do {
320 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
321  assert(read(fd, &randval, sizeof(randval)) == sizeof(randval));
322 #else
323  randval = QRandomGenerator::system()->generate();
324 #endif
325  } while (randval < max_mod_bound);
326 
327  return randval % bound;
328 }
329 
330 QString Pass::generateRandomPassword(const QString &charset,
331  unsigned int length) {
332  QString out;
333  for (unsigned int i = 0; i < length; ++i) {
334  out.append(charset.at(static_cast<int>(
335  boundedRandom(static_cast<quint32>(charset.length())))));
336  }
337  return out;
338 }
void finishedMove(const QString &, const QString &)
void finishedCopy(const QString &, const QString &)
QDateTime expiry
UserInfo::expiry date/time key expires.
Definition: userinfo.h:54
static bool isLessRandom(const bool &defaultValue=QVariant().toBool())
QDateTime created
UserInfo::created date/time key was created.
Definition: userinfo.h:58
void GenerateGPGKeys(QString batch)
Pass::GenerateGPGKeys internal gpg keypair generator . .
Definition: pass.cpp:115
PROCESS
Definition: enums.h:16
QList< UserInfo > listKeys(QStringList keystring, bool secret=false)
Pass::listKeys list users.
Definition: pass.cpp:129
static QString getGpgHome(const QString &defaultValue=QVariant().toString())
void finishedGitPush(const QString &, const QString &)
#define dbg()
Definition: debughelper.h:7
void setEnvironment(const QStringList &env)
Executor::setEnvironment set environment variables for executor processes.
Definition: executor.cpp:183
QString key_id
UserInfo::key_id hexadecimal representation.
Definition: userinfo.h:36
void finishedInsert(const QString &, const QString &)
QString generateRandomPassword(const QString &charset, unsigned int length)
Definition: pass.cpp:330
Executes external commands for handleing password, git and other data.
Definition: executor.h:12
static bool isAvoidNumbers(const bool &defaultValue=QVariant().toBool())
char validity
UserInfo::validity GnuPG representation of validity http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob_plain;f=doc/DETAILS.
Definition: userinfo.h:41
Pass()
Pass::Pass wrapper for using either pass or the pass imitation.
Definition: pass.cpp:17
void finishedRemove(const QString &, const QString &)
Executor exec
Definition: pass.h:32
void execute(int id, const QString &app, const QStringList &args, bool readStdout, bool readStderr=true)
Executor::execute execute an app.
Definition: executor.cpp:58
static QString getPassStore(const QString &defaultValue=QVariant().toString())
Enumerators for configuration and runtime items.
void finishedGitPull(const QString &, const QString &)
static QStringList getRecipientString(QString for_file, QString separator=" ", int *count=NULL)
Pass::getRecipientString formated string for use with GPG.
Definition: pass.cpp:294
void critical(QString, QString)
virtual void finished(int id, int exitCode, const QString &out, const QString &err)
Pass::processFinished reemits specific signal based on what process has finished. ...
Definition: pass.cpp:187
void init()
Definition: pass.cpp:46
static QStringList getRecipientList(QString for_file)
Pass::getRecipientList return list of gpg-id&#39;s to encrypt for.
Definition: pass.cpp:258
static bool isAvoidCapitals(const bool &defaultValue=QVariant().toBool())
void starting()
starting signal that is emited when process starts
virtual QString Generate_b(unsigned int length, const QString &charset)
Pass::const Generat&e use eithconst er pwge&n or internal password generator.
Definition: pass.cpp:70
void finishedInit(const QString &, const QString &)
static bool isUsePwgen(const bool &defaultValue=QVariant().toBool())
static QString getPwgenExecutable(const QString &defaultValue=QVariant().toString())
static bool isUseSymbols(const bool &defaultValue=QVariant().toBool())
void executeWrapper(PROCESS id, const QString &app, const QStringList &args, bool readStdout=true, bool readStderr=true)
Definition: pass.cpp:30
quint32 boundedRandom(quint32 bound)
Definition: pass.cpp:304
void updateEnv()
Pass::updateEnv update the execution environment (used when switching profiles)
Definition: pass.cpp:237
QString name
UserInfo::name full name.
Definition: userinfo.h:32
Stores key info lines including validity, creation date and more.
Definition: userinfo.h:11
static QString getGpgExecutable(const QString &defaultValue=QVariant().toString())
void finishedShow(const QString &)
void finishedGitInit(const QString &, const QString &)
void startingExecuteWrapper()
void processErrorExit(int exitCode, const QString &err)
void finishedOtpGenerate(const QString &)
int executeBlocking(QString app, const QStringList &args, QString input=QString(), QString *process_out=Q_NULLPTR, QString *process_err=Q_NULLPTR)