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 #include "debughelper.h"
3 #include "qtpasssettings.h"
4 #include "util.h"
5 
6 using namespace std;
7 using namespace Enums;
8 
12 Pass::Pass() : wrapperRunning(false), env(QProcess::systemEnvironment()) {
13  connect(&exec,
14  static_cast<void (Executor::*)(int, int, const QString &,
15  const QString &)>(&Executor::finished),
16  this, &Pass::finished);
17 
18  // TODO(bezet): stop using process
19  // connect(&process, SIGNAL(error(QProcess::ProcessError)), this,
20  // SIGNAL(error(QProcess::ProcessError)));
21 
23 }
24 
25 void Pass::executeWrapper(PROCESS id, const QString &app,
26  const QStringList &args, bool readStdout,
27  bool readStderr) {
28  executeWrapper(id, app, args, QString(), readStdout, readStderr);
29 }
30 
31 void Pass::executeWrapper(PROCESS id, const QString &app,
32  const QStringList &args, QString input,
33  bool readStdout, bool readStderr) {
34  dbg() << app << args;
35  exec.execute(id, QtPassSettings::getPassStore(), app, args, input, readStdout,
36  readStderr);
37 }
38 
39 void Pass::init() {
40 #ifdef __APPLE__
41  // If it exists, add the gpgtools to PATH
42  if (QFile("/usr/local/MacGPG2/bin").exists())
43  env.replaceInStrings("PATH=", "PATH=/usr/local/MacGPG2/bin:");
44  // Add missing /usr/local/bin
45  if (env.filter("/usr/local/bin").isEmpty())
46  env.replaceInStrings("PATH=", "PATH=/usr/local/bin:");
47 #endif
48 
49  if (!QtPassSettings::getGpgHome().isEmpty()) {
50  QDir absHome(QtPassSettings::getGpgHome());
51  absHome.makeAbsolute();
52  env << "GNUPGHOME=" + absHome.path();
53  }
54 }
55 
63 QString Pass::Generate_b(unsigned int length, const QString &charset) {
64  QString passwd;
66  // --secure goes first as it overrides --no-* otherwise
67  QStringList args;
68  args.append("-1");
70  args.append("--secure");
71  args.append(QtPassSettings::isAvoidCapitals() ? "--no-capitalize"
72  : "--capitalize");
73  args.append(QtPassSettings::isAvoidNumbers() ? "--no-numerals"
74  : "--numerals");
76  args.append("--symbols");
77  args.append(QString::number(length));
78  QString p_out;
79  // TODO(bezet): try-catch here(2 statuses to merge o_O)
81  &passwd) == 0)
82  passwd.remove(QRegExp("[\\n\\r]"));
83  else {
84  passwd.clear();
85  qDebug() << __FILE__ << ":" << __LINE__ << "\t"
86  << "pwgen fail";
87  // TODO(bezet): emit critical ?
88  }
89  } else {
90  if (charset.length() > 0) {
91  passwd = generateRandomPassword(charset, length);
92  } else {
93  emit critical(
94  tr("No characters chosen"),
95  tr("Can't generate password, there are no characters to choose from "
96  "set in the configuration!"));
97  }
98  }
99  return passwd;
100 }
101 
106 void Pass::GenerateGPGKeys(QString batch) {
108  {"--gen-key", "--no-tty", "--batch"}, batch);
109  // TODO check status / error messages - probably not here, it's just started
110  // here, see finished for details
111  // https://github.com/IJHack/QtPass/issues/202#issuecomment-251081688
112 }
113 
120 QList<UserInfo> Pass::listKeys(QString keystring, bool secret) {
121  QList<UserInfo> users;
122  QStringList args = {"--no-tty", "--with-colons"};
123  args.append(secret ? "--list-secret-keys" : "--list-keys");
124  if (!keystring.isEmpty())
125  args.append(keystring);
126  QString p_out;
128  0)
129  return users;
130  QStringList keys = p_out.split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
131  UserInfo current_user;
132  foreach (QString key, keys) {
133  QStringList props = key.split(':');
134  if (props.size() < 10)
135  continue;
136  if (props[0] == (secret ? "sec" : "pub")) {
137  if (!current_user.key_id.isEmpty())
138  users.append(current_user);
139  current_user = UserInfo();
140  current_user.key_id = props[4];
141  current_user.name = props[9].toUtf8();
142  current_user.validity = props[1][0].toLatin1();
143  current_user.created.setTime_t(props[5].toUInt());
144  current_user.expiry.setTime_t(props[6].toUInt());
145  } else if (current_user.name.isEmpty() && props[0] == "uid") {
146  current_user.name = props[9];
147  }
148  }
149  if (!current_user.key_id.isEmpty())
150  users.append(current_user);
151  return users;
152 }
153 
164 void Pass::finished(int id, int exitCode, const QString &out,
165  const QString &err) {
166  PROCESS pid = static_cast<PROCESS>(id);
167  if (exitCode != 0) {
168  emit processErrorExit(exitCode, err);
169  return;
170  }
171  switch (pid) {
172  case GIT_INIT:
173  emit finishedGitInit(out, err);
174  break;
175  case GIT_PULL:
176  emit finishedGitPull(out, err);
177  break;
178  case GIT_PUSH:
179  emit finishedGitPush(out, err);
180  break;
181  case PASS_SHOW:
182  emit finishedShow(out);
183  break;
184  case PASS_OTP_GENERATE:
185  emit finishedOtpGenerate(out);
186  break;
187  case PASS_INSERT:
188  emit finishedInsert(out, err);
189  break;
190  case PASS_REMOVE:
191  emit finishedRemove(out, err);
192  break;
193  case PASS_INIT:
194  emit finishedInit(out, err);
195  break;
196  case PASS_MOVE:
197  emit finishedMove(out, err);
198  break;
199  case PASS_COPY:
200  emit finishedCopy(out, err);
201  break;
202  default:
203  dbg() << "Unhandled process type" << pid;
204  break;
205  }
206 }
207 
213  QStringList store = env.filter("PASSWORD_STORE_DIR");
214  // put PASSWORD_STORE_DIR in env
215  if (store.isEmpty()) {
216  // dbg()<< "Added
217  // PASSWORD_STORE_DIR";
218  env.append("PASSWORD_STORE_DIR=" + QtPassSettings::getPassStore());
219  } else {
220  // dbg()<< "Update
221  // PASSWORD_STORE_DIR with " + passStore;
222  env.replaceInStrings(store.first(), "PASSWORD_STORE_DIR=" +
224  }
225  exec.setEnvironment(env);
226 }
227 
233 QStringList Pass::getRecipientList(QString for_file) {
234  QDir gpgIdPath(QFileInfo(for_file.startsWith(QtPassSettings::getPassStore())
235  ? for_file
236  : QtPassSettings::getPassStore() + for_file)
237  .absoluteDir());
238  bool found = false;
239  while (gpgIdPath.exists() &&
240  gpgIdPath.absolutePath().startsWith(QtPassSettings::getPassStore())) {
241  if (QFile(gpgIdPath.absoluteFilePath(".gpg-id")).exists()) {
242  found = true;
243  break;
244  }
245  if (!gpgIdPath.cdUp())
246  break;
247  }
248  QFile gpgId(found ? gpgIdPath.absoluteFilePath(".gpg-id")
249  : QtPassSettings::getPassStore() + ".gpg-id");
250  if (!gpgId.open(QIODevice::ReadOnly | QIODevice::Text))
251  return QStringList();
252  QStringList recipients;
253  while (!gpgId.atEnd()) {
254  QString recipient(gpgId.readLine());
255  recipient = recipient.trimmed();
256  if (!recipient.isEmpty())
257  recipients += recipient;
258  }
259  return recipients;
260 }
261 
269 QString Pass::getRecipientString(QString for_file, QString separator,
270  int *count) {
271  QString recipients_str;
272  QStringList recipients_list = Pass::getRecipientList(for_file);
273  if (count)
274  *count = recipients_list.size();
275  foreach (const QString recipient, recipients_list)
276  recipients_str += separator + '"' + recipient + '"';
277  return recipients_str;
278 }
279 
280 /* Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
281  */
282 
283 quint32 Pass::boundedRandom(quint32 bound) {
284  if (bound < 2) {
285  return 0;
286  }
287 
288  quint32 randval;
289  const quint32 max_mod_bound = (1 + ~bound) % bound;
290 
291 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
292  static int fd = -1;
293  if (fd == -1) {
294  assert((fd = open("/dev/urandom", O_RDONLY)) >= 0);
295  }
296 #endif
297 
298  do {
299 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
300  assert(read(fd, &randval, sizeof(randval)) == sizeof(randval));
301 #else
302  randval = QRandomGenerator::system()->generate();
303 #endif
304  } while (randval < max_mod_bound);
305 
306  return randval % bound;
307 }
308 
309 QString Pass::generateRandomPassword(const QString &charset,
310  unsigned int length) {
311  QString out;
312  for (unsigned int i = 0; i < length; ++i) {
313  out.append(charset.at(static_cast<int>(
314  boundedRandom(static_cast<quint32>(charset.length())))));
315  }
316  return out;
317 }
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:106
PROCESS
Definition: enums.h:16
static QString getGpgHome(const QString &defaultValue=QVariant().toString())
void finishedGitPush(const QString &, const QString &)
#define dbg()
Definition: debughelper.h:7
QList< UserInfo > listKeys(QString keystring="", bool secret=false)
Pass::listKeys list users.
Definition: pass.cpp:120
void setEnvironment(const QStringList &env)
Executor::setEnvironment set environment variables for executor processes.
Definition: executor.cpp:172
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:309
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:12
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:51
static QString getPassStore(const QString &defaultValue=QVariant().toString())
Enumerators for configuration and runtime items.
void finishedGitPull(const QString &, const QString &)
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:164
static QString getRecipientString(QString for_file, QString separator=" ", int *count=NULL)
Pass::getRecipientString formated string for use with GPG.
Definition: pass.cpp:269
void init()
Definition: pass.cpp:39
static QStringList getRecipientList(QString for_file)
Pass::getRecipientList return list of gpg-id&#39;s to encrypt for.
Definition: pass.cpp:233
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::Generate use either pwgen or internal password generator.
Definition: pass.cpp:63
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:25
quint32 boundedRandom(quint32 bound)
Definition: pass.cpp:283
void updateEnv()
Pass::updateEnv update the execution environment (used when switching profiles)
Definition: pass.cpp:212
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)
Executor::executeBlocking blocking version of the executor, takes input and presents it as stdin...
Definition: executor.cpp:125