QtPass 1.4.0
Multi-platform GUI for pass, the standard unix password manager.
Loading...
Searching...
No Matches
pass.cpp
Go to the documentation of this file.
1#include "pass.h"
2#include "qtpasssettings.h"
3#include <QDir>
4#include <QRandomGenerator>
5#include <QRegularExpression>
6
7#ifdef QT_DEBUG
8#include "debughelper.h"
9#endif
10
11using namespace std;
12using namespace Enums;
13
17Pass::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 env.append("WSLENV=PASSWORD_STORE_DIR/p");
29}
30
31void Pass::executeWrapper(PROCESS id, const QString &app,
32 const QStringList &args, bool readStdout,
33 bool readStderr) {
34 executeWrapper(id, app, args, QString(), readStdout, readStderr);
35}
36
37void Pass::executeWrapper(PROCESS id, const QString &app,
38 const QStringList &args, QString input,
39 bool readStdout, bool readStderr) {
40#ifdef QT_DEBUG
41 dbg() << app << args;
42#endif
43 exec.execute(id, QtPassSettings::getPassStore(), app, args, input, readStdout,
44 readStderr);
45}
46
47void Pass::init() {
48#ifdef __APPLE__
49 // If it exists, add the gpgtools to PATH
50 if (QFile("/usr/local/MacGPG2/bin").exists())
51 env.replaceInStrings("PATH=", "PATH=/usr/local/MacGPG2/bin:");
52 // Add missing /usr/local/bin
53 if (env.filter("/usr/local/bin").isEmpty())
54 env.replaceInStrings("PATH=", "PATH=/usr/local/bin:");
55#endif
56
57 if (!QtPassSettings::getGpgHome().isEmpty()) {
58 QDir absHome(QtPassSettings::getGpgHome());
59 absHome.makeAbsolute();
60 env << "GNUPGHOME=" + absHome.path();
61 }
62}
63
71QString Pass::Generate_b(unsigned int length, const QString &charset) {
72 QString passwd;
74 // --secure goes first as it overrides --no-* otherwise
75 QStringList args;
76 args.append("-1");
78 args.append("--secure");
79 args.append(QtPassSettings::isAvoidCapitals() ? "--no-capitalize"
80 : "--capitalize");
81 args.append(QtPassSettings::isAvoidNumbers() ? "--no-numerals"
82 : "--numerals");
84 args.append("--symbols");
85 args.append(QString::number(length));
86 // TODO(bezet): try-catch here(2 statuses to merge o_O)
88 &passwd) == 0) {
89 static const QRegularExpression literalNewLines{"[\\n\\r]"};
90 passwd.remove(literalNewLines);
91 } else {
92 passwd.clear();
93#ifdef QT_DEBUG
94 qDebug() << __FILE__ << ":" << __LINE__ << "\t"
95 << "pwgen fail";
96#endif
97 // TODO(bezet): emit critical ?
98 }
99 } else {
100 if (charset.length() > 0) {
101 passwd = generateRandomPassword(charset, length);
102 } else {
103 emit critical(
104 tr("No characters chosen"),
105 tr("Can't generate password, there are no characters to choose from "
106 "set in the configuration!"));
107 }
108 }
109 return passwd;
110}
111
116void Pass::GenerateGPGKeys(QString batch) {
118 {"--gen-key", "--no-tty", "--batch"}, batch);
119 // TODO check status / error messages - probably not here, it's just started
120 // here, see finished for details
121 // https://github.com/IJHack/QtPass/issues/202#issuecomment-251081688
122}
123
130QList<UserInfo> Pass::listKeys(QStringList keystrings, bool secret) {
131 QList<UserInfo> users;
132 QStringList args = {"--no-tty", "--with-colons", "--with-fingerprint"};
133 args.append(secret ? "--list-secret-keys" : "--list-keys");
134
135 for (const QString &keystring : qAsConst(keystrings)) {
136 if (!keystring.isEmpty()) {
137 args.append(keystring);
138 }
139 }
140 QString p_out;
142 0)
143 return users;
144 static const QRegularExpression newLines{"[\r\n]"};
145#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
146 const QStringList keys = p_out.split(newLines, Qt::SkipEmptyParts);
147#else
148 const QStringList keys = p_out.split(newLines, QString::SkipEmptyParts);
149#endif
150 UserInfo current_user;
151 for (const QString &key : keys) {
152 QStringList props = key.split(':');
153 if (props.size() < 10)
154 continue;
155 if (props[0] == (secret ? "sec" : "pub")) {
156 if (!current_user.key_id.isEmpty())
157 users.append(current_user);
158 current_user = UserInfo();
159 current_user.key_id = props[4];
160 current_user.name = props[9].toUtf8();
161 current_user.validity = props[1][0].toLatin1();
162 current_user.created.setSecsSinceEpoch(props[5].toUInt());
163 current_user.expiry.setSecsSinceEpoch(props[6].toUInt());
164 } else if (current_user.name.isEmpty() && props[0] == "uid") {
165 current_user.name = props[9];
166 } else if ((props[0] == "fpr") && props[9].endsWith(current_user.key_id)) {
167 current_user.key_id = props[9];
168 }
169 }
170 if (!current_user.key_id.isEmpty())
171 users.append(current_user);
172 return users;
173}
174
181QList<UserInfo> Pass::listKeys(QString keystring, bool secret) {
182 return listKeys(QStringList(keystring), secret);
183}
184
195void Pass::finished(int id, int exitCode, const QString &out,
196 const QString &err) {
197 auto pid = static_cast<PROCESS>(id);
198 if (exitCode != 0) {
199 emit processErrorExit(exitCode, err);
200 return;
201 }
202 switch (pid) {
203 case GIT_INIT:
204 emit finishedGitInit(out, err);
205 break;
206 case GIT_PULL:
207 emit finishedGitPull(out, err);
208 break;
209 case GIT_PUSH:
210 emit finishedGitPush(out, err);
211 break;
212 case PASS_SHOW:
213 emit finishedShow(out);
214 break;
216 emit finishedOtpGenerate(out);
217 break;
218 case PASS_INSERT:
219 emit finishedInsert(out, err);
220 break;
221 case PASS_REMOVE:
222 emit finishedRemove(out, err);
223 break;
224 case PASS_INIT:
225 emit finishedInit(out, err);
226 break;
227 case PASS_MOVE:
228 emit finishedMove(out, err);
229 break;
230 case PASS_COPY:
231 emit finishedCopy(out, err);
232 break;
233 case GPG_GENKEYS:
234 emit finishedGenerateGPGKeys(out, err);
235 break;
236 default:
237#ifdef QT_DEBUG
238 dbg() << "Unhandled process type" << pid;
239#endif
240 break;
241 }
242}
243
249 // put PASSWORD_STORE_SIGNING_KEY in env
250 QStringList envSigningKey = env.filter("PASSWORD_STORE_SIGNING_KEY=");
251 QString currentSigningKey = QtPassSettings::getPassSigningKey();
252 if (envSigningKey.isEmpty()) {
253 if (!currentSigningKey.isEmpty()) {
254 // dbg()<< "Added
255 // PASSWORD_STORE_SIGNING_KEY with" + currentSigningKey;
256 env.append("PASSWORD_STORE_SIGNING_KEY=" + currentSigningKey);
257 }
258 } else {
259 if (currentSigningKey.isEmpty()) {
260 // dbg() << "Removed
261 // PASSWORD_STORE_SIGNING_KEY";
262 env.removeAll(envSigningKey.first());
263 } else {
264 // dbg()<< "Update
265 // PASSWORD_STORE_SIGNING_KEY with " + currentSigningKey;
266 env.replaceInStrings(envSigningKey.first(),
267 "PASSWORD_STORE_SIGNING_KEY=" + currentSigningKey);
268 }
269 }
270 // put PASSWORD_STORE_DIR in env
271 QStringList store = env.filter("PASSWORD_STORE_DIR=");
272 if (store.isEmpty()) {
273 // dbg()<< "Added
274 // PASSWORD_STORE_DIR";
275 env.append("PASSWORD_STORE_DIR=" + QtPassSettings::getPassStore());
276 } else {
277 // dbg()<< "Update
278 // PASSWORD_STORE_DIR with " + passStore;
279 env.replaceInStrings(store.first(), "PASSWORD_STORE_DIR=" +
281 }
282 exec.setEnvironment(env);
283}
284
290QString Pass::getGpgIdPath(QString for_file) {
291 QString passStore =
292 QDir::fromNativeSeparators(QtPassSettings::getPassStore());
293 QDir gpgIdDir(
294 QFileInfo(QDir::fromNativeSeparators(for_file).startsWith(passStore)
295 ? for_file
296 : QtPassSettings::getPassStore() + for_file)
297 .absoluteDir());
298 bool found = false;
299 while (gpgIdDir.exists() && gpgIdDir.absolutePath().startsWith(passStore)) {
300 if (QFile(gpgIdDir.absoluteFilePath(".gpg-id")).exists()) {
301 found = true;
302 break;
303 }
304 if (!gpgIdDir.cdUp())
305 break;
306 }
307 QString gpgIdPath(found ? gpgIdDir.absoluteFilePath(".gpg-id")
308 : QtPassSettings::getPassStore() + ".gpg-id");
309
310 return gpgIdPath;
311}
312
318QStringList Pass::getRecipientList(QString for_file) {
319 QFile gpgId(getGpgIdPath(for_file));
320 if (!gpgId.open(QIODevice::ReadOnly | QIODevice::Text))
321 return QStringList();
322 QStringList recipients;
323 while (!gpgId.atEnd()) {
324 QString recipient(gpgId.readLine());
325 recipient = recipient.trimmed();
326 if (!recipient.isEmpty())
327 recipients += recipient;
328 }
329 return recipients;
330}
331
339QStringList Pass::getRecipientString(QString for_file, QString separator,
340 int *count) {
341 Q_UNUSED(separator)
342 Q_UNUSED(count)
343 return Pass::getRecipientList(for_file);
344}
345
346/* Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
347 */
348
349quint32 Pass::boundedRandom(quint32 bound) {
350 if (bound < 2) {
351 return 0;
352 }
353
354 quint32 randval;
355 const quint32 max_mod_bound = (1 + ~bound) % bound;
356
357 do {
358 randval = QRandomGenerator::system()->generate();
359 } while (randval < max_mod_bound);
360
361 return randval % bound;
362}
363
364QString Pass::generateRandomPassword(const QString &charset,
365 unsigned int length) {
366 QString out;
367 for (unsigned int i = 0; i < length; ++i) {
368 out.append(charset.at(static_cast<int>(
369 boundedRandom(static_cast<quint32>(charset.length())))));
370 }
371 return out;
372}
Executes external commands for handleing password, git and other data.
Definition: executor.h:12
static 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:171
void execute(int id, const QString &app, const QStringList &args, bool readStdout, bool readStderr=true)
Executor::execute execute an app.
Definition: executor.cpp:68
void setEnvironment(const QStringList &env)
Executor::setEnvironment set environment variables for executor processes.
Definition: executor.cpp:223
void starting()
starting signal that is emited when process starts
QString generateRandomPassword(const QString &charset, unsigned int length)
Definition: pass.cpp:364
void init()
Definition: pass.cpp:47
void startingExecuteWrapper()
void GenerateGPGKeys(QString batch)
Pass::GenerateGPGKeys internal gpg keypair generator . .
Definition: pass.cpp:116
void finishedCopy(const QString &, const QString &)
void finishedShow(const QString &)
void finishedRemove(const QString &, const QString &)
void finishedMove(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:339
void critical(QString, QString)
void finishedGitInit(const QString &, const QString &)
Pass()
Pass::Pass wrapper for using either pass or the pass imitation.
Definition: pass.cpp:17
void executeWrapper(PROCESS id, const QString &app, const QStringList &args, bool readStdout=true, bool readStderr=true)
Definition: pass.cpp:31
static QStringList getRecipientList(QString for_file)
Pass::getRecipientList return list of gpg-id's to encrypt for.
Definition: pass.cpp:318
void finishedInsert(const QString &, const QString &)
void finishedInit(const QString &, const QString &)
void finishedOtpGenerate(const QString &)
Executor exec
Definition: pass.h:25
virtual QString Generate_b(unsigned int length, const QString &charset)
Pass::Generate use either pwgen or internal password generator.
Definition: pass.cpp:71
static QString getGpgIdPath(QString for_file)
Pass::getGpgIdPath return gpgid file path for some file (folder).
Definition: pass.cpp:290
void processErrorExit(int exitCode, const QString &err)
quint32 boundedRandom(quint32 bound)
Definition: pass.cpp:349
void finishedGitPull(const QString &, const QString &)
void finishedGitPush(const QString &, const QString &)
QList< UserInfo > listKeys(QStringList keystrings, bool secret=false)
Pass::listKeys list users.
Definition: pass.cpp:130
void updateEnv()
Pass::updateEnv update the execution environment (used when switching profiles)
Definition: pass.cpp:248
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:195
void finishedGenerateGPGKeys(const QString &, const QString &)
static QString getPassSigningKey(const QString &defaultValue=QVariant().toString())
static bool isUseSymbols(const bool &defaultValue=QVariant().toBool())
static bool isLessRandom(const bool &defaultValue=QVariant().toBool())
static bool isAvoidNumbers(const bool &defaultValue=QVariant().toBool())
static QString getPwgenExecutable(const QString &defaultValue=QVariant().toString())
static bool isUsePwgen(const bool &defaultValue=QVariant().toBool())
static bool isAvoidCapitals(const bool &defaultValue=QVariant().toBool())
static QString getGpgExecutable(const QString &defaultValue=QVariant().toString())
static QString getPassStore(const QString &defaultValue=QVariant().toString())
static QString getGpgHome(const QString &defaultValue=QVariant().toString())
#define dbg()
Definition: debughelper.h:7
Enumerators for configuration and runtime items.
PROCESS
Definition: enums.h:16
@ PASS_INIT
Definition: enums.h:26
@ PASS_OTP_GENERATE
Definition: enums.h:34
@ PASS_INSERT
Definition: enums.h:24
@ GIT_INIT
Definition: enums.h:17
@ PASS_COPY
Definition: enums.h:29
@ PASS_MOVE
Definition: enums.h:28
@ GPG_GENKEYS
Definition: enums.h:27
@ PASS_REMOVE
Definition: enums.h:25
@ PASS_SHOW
Definition: enums.h:23
@ GIT_PULL
Definition: enums.h:21
@ GIT_PUSH
Definition: enums.h:22
Stores key info lines including validity, creation date and more.
Definition: userinfo.h:11
QString key_id
UserInfo::key_id hexadecimal representation.
Definition: userinfo.h:36
QDateTime created
UserInfo::created date/time key was created.
Definition: userinfo.h:58
QString name
UserInfo::name full name.
Definition: userinfo.h:32
char validity
UserInfo::validity GnuPG representation of validity http://git.gnupg.org/cgi-bin/gitweb....
Definition: userinfo.h:41
QDateTime expiry
UserInfo::expiry date/time key expires.
Definition: userinfo.h:54