2 Copyright (C) 2010-2024 Ben Kibbey <bjk@luxsci.net>
4 This file is part of qpwmc.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22 * This is a generic class that wraps around the most used libpwmd functions.
23 * It should serve its purpose for most applications and when it doesn't you
24 * can reimplement it or use the handle() to do what's needed.
33 #include <QStringList>
36 #include "pwmdRemoteHost.h"
38 /* Internal command ID's are < 0 although they may still be passed to
39 * the commandResult() signal. */
40 #define PwmdCmdIdInvalid -255
41 #define PwmdCmdIdRunQueue -128 // Deprecated
42 #define PwmdCmdIdInternalCloseFile -8
43 #define PwmdCmdIdInternalConnectHost -7
44 #define PwmdCmdIdInternalGenKey -6
45 #define PwmdCmdIdInternalStatusError -5
46 #define PwmdCmdIdInternalConnect -4
47 #define PwmdCmdIdInternalOpen -3
48 #define PwmdCmdIdInternalSave -2
49 #define PwmdCmdIdInternalPassword -1
52 class PwmdInquireData
;
54 /* Creates a new command to be passed to the command thread. */
55 class PwmdCommandQueueItem
58 PwmdCommandQueueItem (int id
, QString cmd
= 0, pwmd_inquire_cb_t
= 0,
59 PwmdInquireData
* = 0);
60 ~PwmdCommandQueueItem ();
62 /* The command id which should be >= 0 since internal id's are < 0. */
65 /* Whether this command is flagged as queued. When this command is the first
66 * in the command queue no action is done. You must call Pwmd::runQueue(). */
67 void setQueuedOnly (bool);
70 /* The command and callback function along with callback data. */
72 pwmd_inquire_cb_t
callback();
73 PwmdInquireData
*data();
74 void setData (PwmdInquireData
*);
76 /* A known error code that should not flush the command queue for this
78 void addError (gpg_error_t
);
79 void addError (gpg_err_source_t
, gpg_err_code_t
);
81 /* Check that the passed error code is in the known error code list. */
82 bool checkError (gpg_error_t
);
84 // Return the error list.
85 QList
<gpg_error_t
> errorList ();
87 /* Clear all known error codes. */
88 void clearErrorList ();
91 * Call this after you are done with this command in each receiver of the
92 * Pwmd::commandResult() signal to let the garbage collector delete it.
97 * Set an error string and code from a callback function of a command to be
98 * shown in the UI thread.
100 void setErrorString (QString
);
101 QString
errorString ();
102 void setError (gpg_error_t
);
103 gpg_error_t
error ();
105 // Whether this command requires a server inquire.
106 void setNeedsInquire (bool = true);
107 bool needsInquire ();
109 // Whether this is the final item in the queue with a unique command ID.
113 friend class PwmdCommandThread
;
114 friend class PwmdCommandCollectorThread
;
117 /* Set host data for use with Pwmd::connectHost() and related signals. */
118 void setHostData (PwmdRemoteHost
&);
119 PwmdRemoteHost
&hostData ();
120 void setCommand (QString
);
121 void setFinalItem (bool = true);
125 pwmd_inquire_cb_t _cb
;
126 PwmdInquireData
*_data
;
127 QList
<gpg_error_t
> _errorList
;
130 QString _errorString
;
132 PwmdRemoteHost _hostData
;
137 /* Runs a command queue in a separate thread to prevent blocking the UI. */
138 class PwmdCommandThread
: public QThread
142 PwmdCommandThread (Pwmd
*p
);
143 ~PwmdCommandThread ();
145 void flushQueue (bool = false, gpg_error_t
= 0);
148 bool isQueued (PwmdCommandQueueItem
*);
149 bool setCommand (PwmdCommandQueueItem
*, bool queueOnly
= false);
154 bool hasFinalItem (PwmdCommandQueueItem
*, QList
<PwmdCommandQueueItem
*>);
155 gpg_error_t
connectSocket (PwmdCommandQueueItem
*);
156 gpg_error_t
openFile (PwmdCommandQueueItem
*);
157 gpg_error_t
saveFile (PwmdCommandQueueItem
*);
158 gpg_error_t
genKey (PwmdCommandQueueItem
*);
159 gpg_error_t
changePassword (PwmdCommandQueueItem
*);
160 static gpg_error_t
knownHostCallback (void *data
, const char *host
,
161 const char *key
, size_t len
);
164 QList
<PwmdCommandQueueItem
*>queue
;
168 class PwmdCommandCollectorThread
: public QThread
172 PwmdCommandCollectorThread (Pwmd
*p
);
173 ~PwmdCommandCollectorThread ();
175 void addItem (PwmdCommandQueueItem
*);
177 bool isQueued (PwmdCommandQueueItem
*);
181 QList
<PwmdCommandQueueItem
*> list
;
186 /* Processes status messages when not in a command. It is started once the
187 * connection is established. */
188 class PwmdStatusMessageThread
: public QThread
192 PwmdStatusMessageThread (Pwmd
*p
);
199 class Pwmd
: public QObject
204 * 'sock' is the Url to connect to. 'filename' is the default filename
205 * to open which can be reset with setFilename(). 'clientName' is used
206 * with the pwmd log file.
208 Pwmd (const QString
& filename
= 0, const QString
& clientName
= 0,
209 const QString
& sock
= 0, QObject
*parent
= 0);
212 /* Sent as an argument to the stateChanged() signal. */
215 Init
, Connecting
, Connected
, Opened
218 /* The ERROR status message obtained via gpgme/gnupg/gpg-agent. */
219 gpg_error_t lastError
;
221 /* The current pwmd connection state. */
222 ConnectionState
state ();
224 /* The data pointer passed to the statusMessage signal. */
225 void setStatusMessageData (void *);
226 void *statusMessageData ();
228 /* Returns the currently set socket path. */
231 /* Will not be considered until the next connect(). */
232 void setSocket (const QString
& sock
);
234 /* Returns the currently set data filename. */
238 * To be called before open() and when a new data file is to be
239 * opened. The default the value passed to the constructor.
241 void setFilename (QString filename
);
243 /* Set user data. May be useful when this class is passed to other
246 void setData (QByteArray
);
249 /* If you use pwmd_command() from libpwmd on a handle obtained from
250 * Pwmd:handle(), these may be needed since the status message thread
251 * does socket IO but only after obtaining a mutex.
257 * Sends a command to the pwmd server. When queueOnly is true, the command is
258 * only added to the command queue. When false and there are other commands
259 * in the queue, the queue is run as if runQueue() had been called. Commands
260 * are run in first-in first-out order (FIFO).
262 virtual bool command (PwmdCommandQueueItem
*cmd
, bool queueOnly
= false);
264 /* Runs all queued commands. */
265 virtual void runQueue ();
267 /* Flush all queued commands. */
268 virtual void flushQueue ();
270 /* Returns the number of queued commands. */
271 virtual unsigned queued ();
273 /* Returns true when command 'id' or 'cmd' is in the command queue or waiting
274 * to be garbage collected. */
275 virtual bool isQueued (int id
);
276 virtual bool isQueued (PwmdCommandQueueItem
*cmd
);
278 /* Cancel a connection in progress or running command. */
279 virtual gpg_error_t
cancel ();
281 /* Set positional parameters to pwmd_connect(). The arguments are
282 * added to pwmd_connect() in the order the were added to the list. See the
283 * libpwmd(3) manual page for details about the parameters.
285 virtual void setConnectParameters (QStringList params
);
287 /* Set the data parameter for the knownHost signal. */
288 virtual void setKnownHostData (void *data
);
289 virtual void *knownHostData ();
292 * Connect the socket set with setSocket(). This function uses the
293 * PwmdCmdIdInternalConnect command ID.
295 virtual bool connect (pwmd_inquire_cb_t
= 0, void *data
= 0,
296 bool queueOnly
= false, PwmdCommandQueueItem
** = 0);
298 /* Associates a handle with an existing connected socket. */
299 virtual bool connectFd (int fd
, pwmd_inquire_cb_t
= 0, void *data
= 0,
300 bool queueOnly
= false, PwmdCommandQueueItem
** = 0);
302 /* For use with connectFd() to keep track of the Connecting/Connected states.
303 * You must wait for the connectReady() signal before calling connectFd().
305 virtual bool connectHost (PwmdRemoteHost hostData
, bool queueOnly
= false,
306 PwmdCommandQueueItem
** = 0);
308 /* When set, a function to call when GPG_ERR_BAD_PASSPHRASE is
309 returned during pwmd_open(). The function should return 0 to try
310 again or an error code to cancel (normally
311 GPG_ERR_BAD_PASSPHRASE). The 'data' parameter is set with
312 setBadPassphraseData(). The 'final' parameter is set to true when
313 the callback has been called at least once and before
314 Pwmd::open() returns and in this case the return value of the
315 callback is ignored. This lets the app for example reset any pinentry
316 strings and only when needed.
318 virtual void setBadPassphraseCallback (gpg_error_t (*)(void *data
, bool final
));
319 void setBadPassphraseData (void *);
322 * Opens the filename specified with setFilename(). This function uses the
323 * PwmdCmdIdInternalOpen command ID.
325 virtual bool open (pwmd_inquire_cb_t cb
= 0, PwmdInquireData
* = 0,
326 bool queueOnly
= false, PwmdCommandQueueItem
** = 0);
329 * Generates a new key. The parameter 'args' are any extra arguments to
330 * pass to the pwmd GENKEY protocol command. This function uses the
331 * PwmdCmdIdInternalGenKey command ID.
333 virtual bool genkey (const QString
& args
= QString (),
334 pwmd_inquire_cb_t cb
= 0, PwmdInquireData
* = 0,
335 bool queueOnly
= false, PwmdCommandQueueItem
** = 0);
338 * Saves any changes do disk. The parameter 'args' are any extra arguments to
339 * pass to the pwmd SAVE protocol command. This function uses the
340 * PwmdCmdIdInternalSave command ID.
342 virtual bool save (const QString
& args
= QString (),
343 pwmd_inquire_cb_t cb
= 0, PwmdInquireData
* = 0,
344 bool queueOnly
= false, PwmdCommandQueueItem
** = 0);
347 * Changes the passphrase for a data file or secret key. The parameter 'args'
348 * are any extra arguments to pass to the pwmd PASSWD protocol command. This
349 * function uses the PwmdCmdIdInternalPassword command ID.
351 virtual bool passwd (const QString
& args
= QString (),
352 pwmd_inquire_cb_t cb
= 0, PwmdInquireData
* = 0,
353 bool queueOnly
= false, PwmdCommandQueueItem
** = 0);
356 * Closes any active handle and resets the connection state to Init.
358 virtual gpg_error_t
reset (bool doEmit
= true, bool disco
= true);
361 * Keeps the connection open but closes the opened file. */
362 virtual gpg_error_t
close (bool queueOnly
= false,
363 PwmdCommandQueueItem
** = 0);
365 /* These should be set before opening a file. */
366 void setLockOnOpen (bool = true); // pwmd option LOCK_ON_OPEN
369 /* If you need call other libpwmd functions then you may need this. */
373 * For convienence. Spawn a qpwmc process to select the socket, file and
374 * element path parameters. The 'external' parameter is whether this instance
375 * is from another process other than "qpwmc".
377 * Returns false on failure or true on success and sets result to the line
378 * containing the parameter values. Use extractToken() to parse the result.
380 bool spawnEditor (QString
&result
, QString sock
= 0, QString file
= 0,
381 QString path
= 0, bool external
= true);
384 * For convienence. Parse the string returned by spawnEditor() and return the
385 * token value. Token may be one of: <SOCKET>, <SOCKET-ARG>, <SSH-AGENT>,
386 * <TLS-VERIFY>, <CONNECT-TIMEOUT>, <SOCKET-TIMEOUT>, <FILE> or <PATH>. The
387 * offset parameter is set to the position of the found token in str.
389 static QString
extractToken (const QString str
, const QString token
,
393 /* Shows a descriptive error string of the specified error code. */
394 static void showError (gpg_error_t
, Pwmd
* = 0, bool bulk
= false);
395 static QString
errorString(gpg_error_t
, Pwmd
* = 0);
397 /* Returns the number of receivers for a 'signal'. */
398 int receivers (const char *signal
);
400 /* For convienence. Handles passphrases and key files. */
401 static gpg_error_t
inquireCallback (void *user
, const char *keyword
,
402 gpg_error_t rc
, char **data
,
406 * For convienence. Shows a message box prompting to accept or reject a new
409 static gpg_error_t
knownHostPrompt(void *data
, const char *host
,
410 const char *key
, size_t len
);
413 * Can be set before showError() to show an error string for a TLS error.
418 /* Required for use with connectFd(). After calling connectHost(), wait for
419 * this signal to continue obtaining the file descriptor to pass to
422 void connectReady (PwmdRemoteHost
);
424 /* Emitted when the connection state changes. */
425 void stateChanged (Pwmd::ConnectionState
);
428 * Emitted when a command completes from the command queue. The 'item' is the
429 * item passed to command (), or an internal item using an internal command
430 * ID. In either case the item must not be deleted by the user. Instead, call
431 * PwmdCommandQueueItem::setSeen() in each receiver of this signal when done
432 * with the item to let it be garbage collected.
434 * The 'queued' parameter is whether the item was queued but removed from the
435 * command queue do to a previous command failure. When 'queued' is true the
436 * same 'rc' as the command that failed is passed.
438 void commandResult (PwmdCommandQueueItem
*item
, QString result
,
439 gpg_error_t rc
, bool queued
);
441 /* Emitted when a status message is received from the server. */
442 void statusMessage (QString msg
, void *userData
);
444 /* Emitted at the start and end of a command or connection. The cmdId is the
445 * command that trigged the signal. */
446 void busy (int cmdId
, bool);
449 * Emitted when verification for a hostname is needed when connecting to a
450 * remote SSH pwmd server. The parent object should send the "knownHostRc
451 * (gpg_error_t)" signal and set the gpg_error_t to 0 for a successful
452 * verification of the remote host, or an error code to terminate the
453 * connection. This Pwmd object is already setup to receive the signal.
455 * The 'data' parameter should be set with Pwmd::setKnownHostData().
457 void knownHost (void *data
, const char *host
, const char *hash
, size_t len
);
460 void passphrase (Pwmd
*, QString keyword
, QString desc
, QString prompt
,
462 void passphraseResult (QString passphrase
, QPrivateSignal
);
466 void slotProcessError (QProcess::ProcessError e
);
467 void slotKnownHostRc (gpg_error_t
);
469 void slotPassphrase (Pwmd
*, QString
, QString
, QString
);
470 void slotPassphraseResult (QString
);
475 gpg_error_t
doConnect (bool, pwmd_inquire_cb_t
, void *);
476 static gpg_error_t
statusCallback (void *, const char *);
477 static gpg_error_t
sshPassphraseCallback (void *, const char *, const char *,
479 char *connectParameterValue (int);
480 static gpg_error_t
passphraseCommon (Pwmd
*, const QString
&keyfile
,
481 char **r_passphrase
, size_t *r_size
,
482 QString keyword
, PwmdInquireData
*inq
);
484 friend class PwmdStatusMessageThread
;
485 friend class PwmdCommandThread
;
486 friend class PwmdCommandCollectorThread
;
487 PwmdStatusMessageThread statusMsgThread
;
488 PwmdCommandThread commandThread
;
489 PwmdCommandCollectorThread collectorThread
;
490 QRecursiveMutex
*commandMutex
;
492 ConnectionState _state
;
499 unsigned inquireMaxlen
;
500 QStringList _connectParameters
;
501 gpg_error_t (*badPassphraseCallback
) (void *, bool);
502 void *badPassphraseData
;
506 bool needKnownHostRc
;
507 gpg_error_t knownHostRc
;
509 bool needPassphraseRc
;
510 gpg_error_t passphraseRc
;
511 QString thePassphraseResult
;
512 QString pinentryHint
;
513 QString pinentryInfo
;
515 void *_knownHostData
;
516 void *_statusMessageData
;
519 /* This is used in to handle pinentry loopback mode over an SSH channel, for
520 * key files and for other inquire data when the inquire type is
521 * NoInquirePassphrase. It may also be used generically in your app by passing
522 * this class as the data parameter of an inquire callback such as the static
523 * Pwmd::inquireCallback().
525 * Note that the data of this class must be allocated with the
526 * libpwmd(3) allocators when set upon the ctor or setData(). When this
527 * class is deleted the data will be freed in the dtor.
529 class PwmdInquireData
533 PwmdInquireData (const QString
&, pwm_t
* = 0);
534 PwmdInquireData (pwm_t
* handle
, QString filename
= 0, char *data
= 0,
537 void setFilename (QString
);
538 const QString
& filename ();
539 void setKeyFile (QString
);
540 const QString
& keyFile ();
541 void setEncryptKeyFile (QString
);
542 const QString
& encryptKeyFile ();
543 void setSignKeyFile (QString
);
544 const QString
& signKeyFile ();
545 void setData (char *, size_t);
546 void setData (const QString
&);
549 void setHandle (pwm_t
*);
551 void setUser (QString
);
552 const QString
& user ();
553 void setUser2 (QString
);
554 const QString
& user2 ();
555 void setSent (size_t);
557 void setUserBool (bool);
559 void setUserPtr (void *);
561 void setUserInt (int);
563 void setSaving (bool extended
= false);
564 bool isSaving (bool &extended
);
566 /* For internal commands which may be used in a callback function. */
567 void setCommandItem (PwmdCommandQueueItem
*);
568 PwmdCommandQueueItem
*commandItem ();
572 friend class PwmdCommandThread
;
573 void setUserInternal (QString
);
574 const QString
& userInternal ();
577 QString _encryptKeyFile
;
578 QString _signKeyFile
;
589 QString _userInternal
;
590 PwmdCommandQueueItem
*_item
;
591 void *_internalData
; // for keyfiles.
592 int _internalInt
; // File descriptor for pwmd_connect_fd().
599 Q_DECLARE_METATYPE (Pwmd::ConnectionState
);