QtPass 1.5.1
Multi-platform GUI for pass, the standard unix password manager.
Loading...
Searching...
No Matches
pass.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2016 Anne Jan Brouwer
2// SPDX-License-Identifier: GPL-3.0-or-later
3#include "pass.h"
4#include "helpers.h"
5#include "qtpasssettings.h"
6#include "util.h"
7#include <QDir>
8#include <QRandomGenerator>
9#include <QRegularExpression>
10#include <utility>
11
12#ifdef QT_DEBUG
13#include "debughelper.h"
14#endif
15
16using Enums::GIT_INIT;
17using Enums::GIT_PULL;
18using Enums::GIT_PUSH;
27
31Pass::Pass() : wrapperRunning(false), env(QProcess::systemEnvironment()) {
32 connect(&exec,
33 static_cast<void (Executor::*)(int, int, const QString &,
34 const QString &)>(&Executor::finished),
35 this, &Pass::finished);
36
37 // TODO(bezet): stop using process
38 // connect(&process, SIGNAL(error(QProcess::ProcessError)), this,
39 // SIGNAL(error(QProcess::ProcessError)));
40
42 env.append("WSLENV=PASSWORD_STORE_DIR/p");
43}
44
45void Pass::executeWrapper(PROCESS id, const QString &app,
46 const QStringList &args, bool readStdout,
47 bool readStderr) {
48 executeWrapper(id, app, args, QString(), readStdout, readStderr);
49}
50
51void Pass::executeWrapper(PROCESS id, const QString &app,
52 const QStringList &args, QString input,
53 bool readStdout, bool readStderr) {
54#ifdef QT_DEBUG
55 dbg() << app << args;
56#endif
57 exec.execute(id, QtPassSettings::getPassStore(), app, args, std::move(input),
58 readStdout, readStderr);
59}
60
61void Pass::init() {
62#ifdef __APPLE__
63 // If it exists, add the gpgtools to PATH
64 if (QFile("/usr/local/MacGPG2/bin").exists())
65 env.replaceInStrings("PATH=", "PATH=/usr/local/MacGPG2/bin:");
66 // Add missing /usr/local/bin
67 if (env.filter("/usr/local/bin").isEmpty())
68 env.replaceInStrings("PATH=", "PATH=/usr/local/bin:");
69#endif
70
71 if (!QtPassSettings::getGpgHome().isEmpty()) {
72 QDir absHome(QtPassSettings::getGpgHome());
73 absHome.makeAbsolute();
74 env << "GNUPGHOME=" + absHome.path();
75 }
76}
77
85auto Pass::Generate_b(unsigned int length, const QString &charset) -> QString {
86 QString passwd;
88 // --secure goes first as it overrides --no-* otherwise
89 QStringList args;
90 args.append("-1");
92 args.append("--secure");
93 }
94 args.append(QtPassSettings::isAvoidCapitals() ? "--no-capitalize"
95 : "--capitalize");
96 args.append(QtPassSettings::isAvoidNumbers() ? "--no-numerals"
97 : "--numerals");
99 args.append("--symbols");
100 }
101 args.append(QString::number(length));
102 // TODO(bezet): try-catch here(2 statuses to merge o_O)
104 &passwd) == 0) {
105 static const QRegularExpression literalNewLines{"[\\n\\r]"};
106 passwd.remove(literalNewLines);
107 } else {
108 passwd.clear();
109#ifdef QT_DEBUG
110 qDebug() << __FILE__ << ":" << __LINE__ << "\t"
111 << "pwgen fail";
112#endif
113 // TODO(bezet): emit critical ?
114 }
115 } else {
116 if (charset.length() > 0) {
117 passwd = generateRandomPassword(charset, length);
118 } else {
119 emit critical(
120 tr("No characters chosen"),
121 tr("Can't generate password, there are no characters to choose from "
122 "set in the configuration!"));
123 }
124 }
125 return passwd;
126}
127
132void Pass::GenerateGPGKeys(QString batch) {
134 {"--gen-key", "--no-tty", "--batch"}, std::move(batch));
135 // TODO(annejan): check status / error messages - probably not here, it's just
136 // started
137 // here, see finished for details
138 // https://github.com/IJHack/QtPass/issues/202#issuecomment-251081688
139}
140
147auto Pass::listKeys(QStringList keystrings, bool secret) -> QList<UserInfo> {
148 QList<UserInfo> users;
149 QStringList args = {"--no-tty", "--with-colons", "--with-fingerprint"};
150 args.append(secret ? "--list-secret-keys" : "--list-keys");
151
152 for (const QString &keystring : AS_CONST(keystrings)) {
153 if (!keystring.isEmpty()) {
154 args.append(keystring);
155 }
156 }
157 QString p_out;
159 &p_out) != 0) {
160 return users;
161 }
162#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
163 const QStringList keys =
164 p_out.split(Util::newLinesRegex(), Qt::SkipEmptyParts);
165#else
166 const QStringList keys =
167 p_out.split(Util::newLinesRegex(), QString::SkipEmptyParts);
168#endif
169 UserInfo current_user;
170 for (const QString &key : keys) {
171 QStringList props = key.split(':');
172 if (props.size() < 10) {
173 continue;
174 }
175 if (props[0] == (secret ? "sec" : "pub")) {
176 if (!current_user.key_id.isEmpty()) {
177 users.append(current_user);
178 }
179 current_user = UserInfo();
180 current_user.key_id = props[4];
181 current_user.name = props[9].toUtf8();
182 current_user.validity = props[1][0].toLatin1();
183 current_user.created.setSecsSinceEpoch(props[5].toUInt());
184 current_user.expiry.setSecsSinceEpoch(props[6].toUInt());
185 } else if (current_user.name.isEmpty() && props[0] == "uid") {
186 current_user.name = props[9];
187 } else if ((props[0] == "fpr") && props[9].endsWith(current_user.key_id)) {
188 current_user.key_id = props[9];
189 }
190 }
191 if (!current_user.key_id.isEmpty()) {
192 users.append(current_user);
193 }
194 return users;
195}
196
203auto Pass::listKeys(const QString &keystring, bool secret) -> QList<UserInfo> {
204 return listKeys(QStringList(keystring), secret);
205}
206
217void Pass::finished(int id, int exitCode, const QString &out,
218 const QString &err) {
219 auto pid = static_cast<PROCESS>(id);
220 if (exitCode != 0) {
221 emit processErrorExit(exitCode, err);
222 return;
223 }
224 switch (pid) {
225 case GIT_INIT:
226 emit finishedGitInit(out, err);
227 break;
228 case GIT_PULL:
229 emit finishedGitPull(out, err);
230 break;
231 case GIT_PUSH:
232 emit finishedGitPush(out, err);
233 break;
234 case PASS_SHOW:
235 emit finishedShow(out);
236 break;
237 case PASS_OTP_GENERATE:
238 emit finishedOtpGenerate(out);
239 break;
240 case PASS_INSERT:
241 emit finishedInsert(out, err);
242 break;
243 case PASS_REMOVE:
244 emit finishedRemove(out, err);
245 break;
246 case PASS_INIT:
247 emit finishedInit(out, err);
248 break;
249 case PASS_MOVE:
250 emit finishedMove(out, err);
251 break;
252 case PASS_COPY:
253 emit finishedCopy(out, err);
254 break;
255 case GPG_GENKEYS:
256 emit finishedGenerateGPGKeys(out, err);
257 break;
258 default:
259#ifdef QT_DEBUG
260 dbg() << "Unhandled process type" << pid;
261#endif
262 break;
263 }
264}
265
271 // put PASSWORD_STORE_SIGNING_KEY in env
272 QStringList envSigningKey = env.filter("PASSWORD_STORE_SIGNING_KEY=");
273 QString currentSigningKey = QtPassSettings::getPassSigningKey();
274 if (envSigningKey.isEmpty()) {
275 if (!currentSigningKey.isEmpty()) {
276 // dbg()<< "Added
277 // PASSWORD_STORE_SIGNING_KEY with" + currentSigningKey;
278 env.append("PASSWORD_STORE_SIGNING_KEY=" + currentSigningKey);
279 }
280 } else {
281 if (currentSigningKey.isEmpty()) {
282 // dbg() << "Removed
283 // PASSWORD_STORE_SIGNING_KEY";
284 env.removeAll(envSigningKey.first());
285 } else {
286 // dbg()<< "Update
287 // PASSWORD_STORE_SIGNING_KEY with " + currentSigningKey;
288 env.replaceInStrings(envSigningKey.first(),
289 "PASSWORD_STORE_SIGNING_KEY=" + currentSigningKey);
290 }
291 }
292 // put PASSWORD_STORE_DIR in env
293 QStringList store = env.filter("PASSWORD_STORE_DIR=");
294 if (store.isEmpty()) {
295 // dbg()<< "Added
296 // PASSWORD_STORE_DIR";
297 env.append("PASSWORD_STORE_DIR=" + QtPassSettings::getPassStore());
298 } else {
299 // dbg()<< "Update
300 // PASSWORD_STORE_DIR with " + passStore;
301 env.replaceInStrings(store.first(), "PASSWORD_STORE_DIR=" +
303 }
304 exec.setEnvironment(env);
305}
306
312auto Pass::getGpgIdPath(const QString &for_file) -> QString {
313 QString passStore =
314 QDir::fromNativeSeparators(QtPassSettings::getPassStore());
315 QString normalizedFile = QDir::fromNativeSeparators(for_file);
316 QString fullPath = normalizedFile.startsWith(passStore)
317 ? normalizedFile
318 : passStore + "/" + normalizedFile;
319 QDir gpgIdDir(QFileInfo(fullPath).absoluteDir());
320 bool found = false;
321 while (gpgIdDir.exists() && gpgIdDir.absolutePath().startsWith(passStore)) {
322 if (QFile(gpgIdDir.absoluteFilePath(".gpg-id")).exists()) {
323 found = true;
324 break;
325 }
326 if (!gpgIdDir.cdUp()) {
327 break;
328 }
329 }
330 QString gpgIdPath(found ? gpgIdDir.absoluteFilePath(".gpg-id")
331 : QtPassSettings::getPassStore() + ".gpg-id");
332
333 return gpgIdPath;
334}
335
341auto Pass::getRecipientList(const QString &for_file) -> QStringList {
342 QFile gpgId(getGpgIdPath(for_file));
343 if (!gpgId.open(QIODevice::ReadOnly | QIODevice::Text)) {
344 return {};
345 }
346 QStringList recipients;
347 while (!gpgId.atEnd()) {
348 QString recipient(gpgId.readLine());
349 recipient = recipient.split("#")[0].trimmed();
350 if (!recipient.isEmpty()) {
351 recipients += recipient;
352 }
353 }
354 return recipients;
355}
356
364auto Pass::getRecipientString(const QString &for_file, const QString &separator,
365 int *count) -> QStringList {
366 Q_UNUSED(separator)
367 Q_UNUSED(count)
368 return Pass::getRecipientList(for_file);
369}
370
371/* Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
372 */
373
374auto Pass::boundedRandom(quint32 bound) -> quint32 {
375 if (bound < 2) {
376 return 0;
377 }
378
379 quint32 randval;
380 const quint32 max_mod_bound = (1 + ~bound) % bound;
381
382 do {
383 randval = QRandomGenerator::system()->generate();
384 } while (randval < max_mod_bound);
385
386 return randval % bound;
387}
388
389auto Pass::generateRandomPassword(const QString &charset, unsigned int length)
390 -> QString {
391 QString out;
392 for (unsigned int i = 0; i < length; ++i) {
393 out.append(charset.at(static_cast<int>(
394 boundedRandom(static_cast<quint32>(charset.length())))));
395 }
396 return out;
397}
Executes external commands for handleing password, git and other data.
Definition executor.h:14
static auto executeBlocking(QString app, const QStringList &args, const QString &input=QString(), QString *process_out=nullptr, QString *process_err=nullptr) -> int
Executor::executeBlocking blocking version of the executor, takes input and presents it as stdin.
Definition executor.cpp:177
void execute(int id, const QString &app, const QStringList &args, bool readStdout, bool readStderr=true)
Executor::execute execute an app.
Definition executor.cpp:73
void setEnvironment(const QStringList &env)
Executor::setEnvironment set environment variables for executor processes.
Definition executor.cpp:234
void starting()
starting signal that is emited when process starts
void init()
Definition pass.cpp:61
void startingExecuteWrapper()
void GenerateGPGKeys(QString batch)
Pass::GenerateGPGKeys internal gpg keypair generator . .
Definition pass.cpp:132
void finishedCopy(const QString &, const QString &)
void finishedShow(const QString &)
void finishedRemove(const QString &, const QString &)
void finishedMove(const QString &, const QString &)
auto boundedRandom(quint32 bound) -> quint32
Definition pass.cpp:374
void finishedGitInit(const QString &, const QString &)
Pass()
Pass::Pass wrapper for using either pass or the pass imitation.
Definition pass.cpp:31
static auto getRecipientString(const QString &for_file, const QString &separator=" ", int *count=NULL) -> QStringList
Pass::getRecipientString formated string for use with GPG.
Definition pass.cpp:364
void executeWrapper(PROCESS id, const QString &app, const QStringList &args, bool readStdout=true, bool readStderr=true)
Definition pass.cpp:45
static auto getRecipientList(const QString &for_file) -> QStringList
Pass::getRecipientList return list of gpg-id's to encrypt for.
Definition pass.cpp:341
auto listKeys(QStringList keystrings, bool secret=false) -> QList< UserInfo >
Pass::listKeys list users.
Definition pass.cpp:147
void finishedInsert(const QString &, const QString &)
void finishedInit(const QString &, const QString &)
static auto getGpgIdPath(const QString &for_file) -> QString
Pass::getGpgIdPath return gpgid file path for some file (folder).
Definition pass.cpp:312
void finishedOtpGenerate(const QString &)
virtual auto Generate_b(unsigned int length, const QString &charset) -> QString
Pass::Generate use either pwgen or internal password generator.
Definition pass.cpp:85
Executor exec
Definition pass.h:27
void processErrorExit(int exitCode, const QString &err)
void finishedGitPull(const QString &, const QString &)
void finishedGitPush(const QString &, const QString &)
void updateEnv()
Pass::updateEnv update the execution environment (used when switching profiles)
Definition pass.cpp:270
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:217
void finishedGenerateGPGKeys(const QString &, const QString &)
auto generateRandomPassword(const QString &charset, unsigned int length) -> QString
Definition pass.cpp:389
static auto getPwgenExecutable(const QString &defaultValue=QVariant().toString()) -> QString
static auto getGpgHome(const QString &defaultValue=QVariant().toString()) -> QString
static auto getPassStore(const QString &defaultValue=QVariant().toString()) -> QString
static auto isAvoidCapitals(const bool &defaultValue=QVariant().toBool()) -> bool
static auto getGpgExecutable(const QString &defaultValue=QVariant().toString()) -> QString
static auto getPassSigningKey(const QString &defaultValue=QVariant().toString()) -> QString
static auto isUseSymbols(const bool &defaultValue=QVariant().toBool()) -> bool
static auto isLessRandom(const bool &defaultValue=QVariant().toBool()) -> bool
static auto isUsePwgen(const bool &defaultValue=QVariant().toBool()) -> bool
static auto isAvoidNumbers(const bool &defaultValue=QVariant().toBool()) -> bool
static auto newLinesRegex() -> const QRegularExpression &
Definition util.cpp:214
#define dbg()
Definition debughelper.h:9
#define AS_CONST(x)
Definition helpers.h:9
PROCESS
Definition enums.h:18
@ PASS_INIT
Definition enums.h:28
@ PASS_OTP_GENERATE
Definition enums.h:36
@ PASS_INSERT
Definition enums.h:26
@ GIT_INIT
Definition enums.h:19
@ PASS_COPY
Definition enums.h:31
@ PASS_MOVE
Definition enums.h:30
@ GPG_GENKEYS
Definition enums.h:29
@ PASS_REMOVE
Definition enums.h:27
@ PASS_SHOW
Definition enums.h:25
@ GIT_PULL
Definition enums.h:23
@ GIT_PUSH
Definition enums.h:24
Stores key info lines including validity, creation date and more.
Definition userinfo.h:13
QString key_id
UserInfo::key_id hexadecimal representation.
Definition userinfo.h:38
QDateTime created
UserInfo::created date/time key was created.
Definition userinfo.h:60
QString name
UserInfo::name full name.
Definition userinfo.h:34
char validity
UserInfo::validity GnuPG representation of validity http://git.gnupg.org/cgi-bin/gitweb....
Definition userinfo.h:43
QDateTime expiry
UserInfo::expiry date/time key expires.
Definition userinfo.h:56