File indexing completed on 2024-06-16 06:54:13

0001 /*
0002     SPDX-FileCopyrightText: 2008 Nicola Gigante <>
0003     SPDX-FileCopyrightText: 2009-2012 Dario Freddi <>
0005     SPDX-License-Identifier: LGPL-2.1-or-later
0006 */
0011 #include "kauthcore_export.h"
0013 #include <QDataStream>
0014 #include <QMap>
0015 #include <QSharedDataPointer>
0016 #include <QString>
0017 #include <QVariant>
0019 /**
0020  @namespace KAuth
0022  @section kauth_intro Introduction
0024  The KDE Authorization API allows developers to write desktop applications that
0025  run high-privileged tasks in an easy, secure and cross-platform way.
0026  Previously, if an application had to do administrative tasks, it had to be run
0027  as root, using mechanisms such as sudo or graphical equivalents, or by setting
0028  the executable's setuid bit. This approach has some drawbacks. For example, the
0029  whole application code, including GUI handling and network communication, had
0030  to be done as root. More code that runs as root means more possible security
0031  holes.
0033  The solution is the caller/helper pattern. With this pattern, the privileged
0034  code is isolated in a small helper tool that runs as root. This tool includes
0035  only the few lines of code that actually need to be run with privileges, not
0036  the whole application logic. All the other parts of the application are run as
0037  a normal user, and the helper tool is called when needed, using a secure
0038  mechanism that ensures that the user is authorized to do so. This pattern is
0039  not very easy to implement, because the developer has to deal with a lot of
0040  details about how to authorize the user, how to call the helper with the right
0041  privileges, how to exchange data with the helper, etc.. This is where the new
0042  KDE Authorization API becomes useful. Thanks to this new library, every
0043  developer can implement the caller/helper pattern to write application that
0044  require high privileges, with a few lines of code in an easy, secure and
0045  cross-platform way.
0047  Not only: the library can also be used to lock down some actions in your
0048  application without using a helper but just checking for authorization and
0049  verifying if the user is allowed to perform it.
0051  The KDE Authorization library uses different backends depending on the system
0052  where it's built. As far as the user authorization is concerned, it currently
0053  uses polkit-1 on linux and Authorization Services on Mac OSX, and a Windows
0054  backend will eventually be written, too. At the communication layer, the
0055  library uses D-Bus on every supported platform.
0058  @section kauth_concepts Concepts
0060  There are a few concepts to understand when using the library. Much of those
0061  are carried from underlying APIs such as polkit-1, so if you know something
0062  about them there shouldn't be problems.
0064  An <i>action</i> is a single task that needs to be done by the application. You
0065  refer to an action using an action identifier, which is a string in reverse
0066  domain name syntax (to avoid duplicates). For example, if the date/time control
0067  center module needs to change the date, it would need an action like
0068  "org.kde.datatime.change". If your application has to perform more than one
0069  privileged task, you should configure more than one action. This allows system
0070  administrators to fine tune the policies that allow users to perform your
0071  actions.
0073  The <i>authorization</i> is the process that is executed to decide if a user
0074  can perform an action or not. In order to execute the helper as root, the user
0075  has to be authorized. For example, on linux, che policykit backend will look at
0076  the policykit policy database to see what requirements the user has to meet in
0077  order to execute the action you requested. The policy set for that action could
0078  allow or deny that user, or could say the user has to authenticate in order to
0079  gain the authorization.
0081  The <i>authentication</i> is the process that allows the system to know that
0082  the person is in front of the console is who he says to be. If an action can be
0083  allowed or not depending on the user's identity, it has to be proved by
0084  entering a password or any other identification data the system requires.
0086  A typical session with the authorization API is like this:
0087  - The user want to perform some privileged task
0088  - The application asks the system if the user is authorized.
0089  - The system asks the user to authenticate, if needed, and reply the application.
0090  - The application uses some system-provided mechanism to execute the helper's
0091    code as the root user. Previously, you had to set the setuid bit to do this,
0092    but we have something cool called
0093    "D-Bus activation" that doesn't require the setuid bit and is much more flexible.
0094  - The helper code, immediately after starting, checks if the caller is
0095    authorized to do what it asks. If not the helper immediately exits!
0096  - If the caller is authorized, the helper executes the task and exits.
0097  - The application receives data back from the helper.
0099  All these steps are managed by the library. Following sections will focus on
0100  how to write the helper to implement your actions and how to call the helper
0101  from the application.
0103  @section kauth_helper Writing the helper tool
0105  The first thing you need to do before writing anything is to decide what
0106  actions you need to implement. Every action needs to be identified by a string
0107  in the reverse domain name syntax. This helps to avoid duplicates. An example
0108  of action id is "org.kde.datetime.change" or "org.kde.ksysguard.killprocess".
0109  Action names can only contain lowercase letters and dots (not as the first or
0110  last char).  You also need an identifier for your helper. An application using
0111  the KDE auth api can implement and use more than one helper, implementing
0112  different actions. An helper is uniquely identified in the system context with
0113  a string. It, again, is in reverse domain name syntax to avoid duplicates. A
0114  common approach is to call the helper like the common prefix of your action
0115  names.  For example, the Date/Time kcm module could use a helper called
0116  "org.kde.datetime", to perform actions like "org.kde.datetime.changedate" and
0117  "org.kde.datetime.changetime". This naming convention simplifies the
0118  implementation of the helper.
0120  From the code point of view, the helper is implemented as a QObject subclass.
0121  Every action is implemented by a public slot. In the example/ directory in the
0122  source code tree you find a complete example.  Let's look at that.  The
0123  helper.h file declares the class that implements the helper. It looks like:
0125  @snippet helper.cpp helper_declaration
0127  The slot names are the last part of the action name, without the helper's ID if
0128  it's a prefix, with all the dots replaced by underscores. In this case, the
0129  helper ID is "org.kde.kf6auth.example", so those three slots implement the
0130  actions "", "org.kde.kf6auth.example.write" and
0131  "org.kde.kf6auth.example.longaction". The helper ID doesn't have to appear at
0132  the beginning of the action name, but it's good practice. If you want to extend
0133  MyHelper to implement also a different action like
0134  "org.kde.datetime.changetime", since the helper ID doesn't match you'll have to
0135  implement a slot called org_kde_datetime_changetime().
0137  The slot's signature is fixed: the return type is ActionReply, a class that
0138  allows you to return results, error codes and custom data to the application
0139  when your action has finished to run.
0141  Let's look at the read action implementation. Its purpose is to read files:
0143  @snippet helper.cpp helper_read_action
0145  First, the code creates a default reply object. The default constructor creates
0146  a reply that reports success. Then it gets the filename parameter from the
0147  argument QVariantMap, that has previously been set by the application, before
0148  calling the helper. If it fails to open the file, it creates an ActionReply
0149  object that notifies that some error has happened in the helper, then set the
0150  error code to that returned by QFile and returns. If there is no error, it
0151  reads the file. The contents are added to the reply.
0153  Because this class will be compiled into a standalone executable, we need a
0154  main() function and some code to initialize everything: you don't have to write
0155  it. Instead, you use the KAUTH_HELPER_MAIN() macro that will take care of
0156  everything. It's used like this:
0158  @snippet helper.cpp helper_main
0160  The first parameter is the string containing the helper identifier. Please note
0161  that you need to use this same string in the application's code to tell the
0162  library which helper to call, so please stay away from typos, because we don't
0163  have any way to detect them. The second parameter is the name of the helper's
0164  class.  Your helper, if complex, can be composed of a lot of source files, but
0165  the important thing is to include this macro in at least one of them.
0167  To build the helper, KDE macros provide a function named
0168  kauth_install_helper_files(). Use it in your cmake file like this:
0170  @code
0171  add_executable(<helper_target> your sources...)
0172  target_link_libraries(<helper_target> your libraries...)
0173  install(TARGETS <helper_target> DESTINATION ${KAUTH_HELPER_INSTALL_DIR})
0175  kauth_install_helper_files(<helper_target> <helper_id> <user>)
0176  @endcode
0178  As locale is not inherited, the auth helper will have the text codec explicitly set
0179  to use UTF-8.
0181  The first argument is the cmake target name for the helper executable, which
0182  you have to build and install separately. Make sure to INSTALL THE HELPER IN
0183  @c ${KAUTH_HELPER_INSTALL_DIR}, otherwise @c kauth_install_helper_files will not work. The
0184  second argument is the helper id. Please be sure to don't misspell it, and to
0185  not quote it. The user parameter is the user that the helper has to be run as.
0186  It usually is root, but some actions could require less strict permissions, so
0187  you should use the right user where possible (for example the user apache if
0188  you have to mess with apache settings). Note that the target created by this
0189  macro already links to libkauth and QtCore.
0191  @section kauth_actions Action registration
0193  To be able to authorize the actions, they have to be added to the policy
0194  database. To do this in a cross-platform way, we provide a cmake macro. It
0195  looks like:
0196  @code
0197  kauth_install_actions(<helper_id> <actions definition file>)
0198  @endcode
0200  The action definition file describes which actions are implemented by your code
0201  and which default security options they should have. It is a common text file
0202  in ini format, with one section for each action and some parameters. The
0203  definition for the read action is:
0205  @verbatim
0206  []
0207  Name=Read action
0208  Description=Read action description
0209  Policy=auth_admin
0210  Persistence=session
0211  @endverbatim
0213  The name parameter is a text describing the action for <i>who reads the
0214  file</i>. The description parameter is the message shown to the user in the
0215  authentication dialog. It should be a finite phrase.  The policy attribute
0216  specify the default rule that the user must satisfy to be authorized. Possible
0217  values are:
0218  - yes: the action should be always allowed
0219  - no: the action should be always denied
0220  - auth_self: the user should authenticate as itself
0221  - auth_admin: the user should authenticate as an administrator user
0223  The persistence attribute is optional. It says how long an authorization should
0224  be retained for that action. The values could be:
0225  - session: the authorization persists until the user logs-out
0226  - always: the authorization will persist indefinitely
0228  If this attribute is missing, the authorization will be queried every time.
0230  @note Only the PolicyKit and polkit-1 backends use this attribute.
0231  @warning With the polkit-1 backend, 'session' and 'always' have the same meaning.
0232           They just make the authorization persists for a few minutes.
0234  @section kauth_app Calling the helper from the application
0236  Once the helper is ready, we need to call it from the main application.
0237  In examples/client.cpp you can see how this is done. To create a reference to
0238  an action, an object of type Action has to be created. Every Action object
0239  refers to an action by its action id. Two objects with the same action id will
0240  act on the same action.  With an Action object, you can authorize and execute
0241  the action. To execute an action you need to retrieve an ExecuteJob, which is
0242  a standard KJob that you can run synchronously or asynchronously.
0243  See the KJob documentation (from KCoreAddons) for more details.
0245  The piece of code that calls the action of the previous example is:
0247  @snippet client.cpp client_how_to_call_helper
0249  First of all, it creates the action object specifying the action id. Then it
0250  loads the filename (we want to read a forbidden file) into the arguments()
0251  QVariantMap, which will be directly passed to the helper in the read() slot's
0252  parameter. This example code uses a synchronous call to execute the action and
0253  retrieve the reply. If the reply succeeded, the reply data is retrieved from
0254  the returned QVariantMap object. Please note that you have
0255  to explicitly set the helper ID to the action: this is done for added safety,
0256  to prevent the caller from accidentally invoking a helper, and also because
0257  KAuth actions may be used without a helper attached (the default).
0259  Please note that if your application is calling the helper multiple times it
0260  must do so from the same thread.
0262  @section kauth_async Asynchronous calls, data reporting, and action termination
0264  For a more advanced example, we look at the action
0265  "org.kde.kf6auth.example.longaction" in the example helper. This is an action
0266  that takes a long time to execute, so we need some features:
0267  - The helper needs to regularly send data to the application, to inform about
0268    the execution status.
0269  - The application needs to be able to stop the action execution if the user
0270    stops it or close the application.
0271  The example code follows:
0273  @snippet helper.cpp helper_longaction
0275  In this example, the action is only waiting a "long" time using a loop, but we
0276  can see some interesting line. The progress status is sent to the application
0277  using the HelperSupport::progressStep(int) and
0278  HelperSupport::progressStep(const QVariantMap &) methods.
0279  When those methods are called, the HelperProxy associated with this action
0280  will emit the HelperProxy::progressStep(const QString &, int) and
0281  HelperProxy::progressStepData(const QString &, const QVariantMap &) signals,
0282  respectively, reporting back the data to the application.
0283  The method that takes an integer argument is the one used here.
0284  Its meaning is application dependent, so you can use it as a sort of
0285  percentage. If you want to report custom data back to the application, you
0286  can use the other method that takes a QVariantMap object which is directly
0287  passed to the app.
0289  In this example code, the loop exits when the HelperSupport::isStopped()
0290  returns true. This happens when the application calls the HelperProxy::stopAction()
0291  method on the corresponding action object.
0292  The stopAction() method, this way, asks the helper to
0293  stop the action execution. It's up to the helper to obbey to this request, and
0294  if it does so, it should return from the slot, _not_ exit.
0296  @section kauth_other Other features
0298  It doesn't happen very frequently that you code something that doesn't require
0299  some debugging, and you'll need some tool, even a basic one, to debug your
0300  helper code as well. For this reason, the KDE Authorization library provides a
0301  message handler for the Qt debugging system. This means that every call to
0302  qDebug() & co. will be reported to the application, and printed using the same
0303  qt debugging system, with the same debug level.  If, in the helper code, you
0304  write something like:
0305  @code
0306  qDebug() << "I'm in the helper";
0307  @endcode
0308  You'll see something like this in the <i>application</i>'s output:
0310  @verbatim
0311  Debug message from the helper: I'm in the helper
0312  @endverbatim
0314  Remember that the debug level is preserved, so if you use qFatal() you won't
0315  only abort the helper (which isn't suggested anyway), but also the application.
0317  */
0318 namespace KAuth
0319 {
0320 class ActionReplyData;
0322 /**
0323  * @class ActionReply actionreply.h <KAuth/ActionReply>
0324  *
0325  * @brief Class that encapsulates a reply coming from the helper after executing
0326  * an action
0327  *
0328  * Helper applications will return this to describe the result of the action.
0329  *
0330  * Callers should access the reply though the KAuth::ExecuteJob job.
0331  *
0332  * @since 4.4
0333  */
0334 class KAUTHCORE_EXPORT ActionReply
0335 {
0336 public:
0337     /**
0338      * Enumeration of the different kinds of replies.
0339      */
0340     enum Type {
0341         KAuthErrorType, ///< An error reply generated by the library itself.
0342         HelperErrorType, ///< An error reply generated by the helper.
0343         SuccessType, ///< The action has been completed successfully
0344     };
0346     static const ActionReply SuccessReply(); ///< An empty successful reply. Same as using the default constructor
0347     static const ActionReply HelperErrorReply(); ///< An empty reply with type() == HelperError and errorCode() == -1
0348     static const ActionReply HelperErrorReply(int error); ///< An empty reply with type() == HelperError and error is set to the passed value
0350     static const ActionReply NoResponderReply(); ///< errorCode() == NoResponder
0351     static const ActionReply NoSuchActionReply(); ///< errorCode() == NoSuchAction
0352     static const ActionReply InvalidActionReply(); ///< errorCode() == InvalidAction
0353     static const ActionReply AuthorizationDeniedReply(); ///< errorCode() == AuthorizationDenied
0354     static const ActionReply UserCancelledReply(); ///< errorCode() == UserCancelled
0355     static const ActionReply HelperBusyReply(); ///< errorCode() == HelperBusy
0356     static const ActionReply AlreadyStartedReply(); ///< errorCode() == AlreadyStartedError
0357     static const ActionReply DBusErrorReply(); ///< errorCode() == DBusError
0359     /**
0360      * The enumeration of the possible values of errorCode() when type() is ActionReply::KAuthError
0361      */
0362     enum Error {
0363         NoError = 0, ///< No error.
0364         NoResponderError, ///< The helper responder object hasn't been set. This shouldn't happen if you use the KAUTH_HELPER macro in the helper source
0365         NoSuchActionError, ///< The action you tried to execute doesn't exist.
0366         InvalidActionError, ///< You tried to execute an invalid action object
0367         AuthorizationDeniedError, ///< You don't have the authorization to execute the action
0368         UserCancelledError, ///< Action execution has been cancelled by the user
0369         HelperBusyError, ///< The helper is busy executing another action (or group of actions). Try later
0370         AlreadyStartedError, ///< The action was already started and is currently running
0371         DBusError, ///< An error from D-Bus occurred
0372         BackendError, ///< The underlying backend reported an error
0373     };
0375     /// Default constructor. Sets type() to Success and errorCode() to zero.
0376     ActionReply();
0378     /**
0379      * @brief Constructor to directly set the type.
0380      *
0381      * This constructor directly sets the reply type. You shouldn't need to
0382      * directly call this constructor, because you can use the more convenient
0383      * predefined replies constants. You also shouldn't create a reply with
0384      * the KAuthError type because it's reserved for errors coming from the
0385      * library.
0386      *
0387      * @param type The type of the new reply
0388      */
0389     ActionReply(Type type);
0391     /**
0392      * @brief Constructor that creates a KAuthError reply with a specified error code.
0393      *        Do not use outside the library.
0394      *
0395      * This constructor is for internal use only, since it creates a reply
0396      * with KAuthError type, which is reserved for errors coming from the library.
0397      *
0398      * @param errorCode The error code of the new reply
0399      */
0400     ActionReply(int errorCode);
0402     /// Copy constructor
0403     ActionReply(const ActionReply &reply);
0405     /// Virtual destructor
0406     virtual ~ActionReply();
0408     /**
0409      * @brief Sets the custom data to send back to the application
0410      *
0411      * In the helper's code you can use this function to set an QVariantMap
0412      * with custom data that will be sent back to the application.
0413      *
0414      * @param data The new QVariantMap object.
0415      */
0416     void setData(const QVariantMap &data);
0418     /**
0419      * @brief Returns the custom data coming from the helper.
0420      *
0421      * This method is used to get the object that contains the custom
0422      * data coming from the helper. In the helper's code, you can set it
0423      * using setData() or the convenience method addData().
0424      *
0425      * @return The data coming from (or that will be sent by) the helper
0426      */
0427     QVariantMap data() const;
0429     /**
0430      * @brief Convenience method to add some data to the reply.
0431      *
0432      * This method adds the pair @c key/value to the QVariantMap used to
0433      * report back custom data to the application.
0434      *
0435      * Use this method if you don't want to create a new QVariantMap only to
0436      * add a new entry.
0437      *
0438      * @param key The new entry's key
0439      * @param value The value of the new entry
0440      */
0441     void addData(const QString &key, const QVariant &value);
0443     /// Returns the reply's type
0444     Type type() const;
0446     /**
0447      * @brief Sets the reply type
0448      *
0449      * Every time you create an action reply, you implicitly set a type.
0450      * Default constructed replies or ActionReply::SuccessReply have
0451      * type() == Success.
0452      * ActionReply::HelperErrorReply has type() == HelperError.
0453      * Predefined error replies have type() == KAuthError.
0454      *
0455      * This means you rarely need to change the type after the creation,
0456      * but if you need to, don't set it to KAuthError, because it's reserved
0457      * for errors coming from the library.
0458      *
0459      * @param type The new reply type
0460      */
0461     void setType(Type type);
0463     /// Returns true if type() == Success
0464     bool succeeded() const;
0466     /// Returns true if type() != Success
0467     bool failed() const;
0469     /**
0470      * @brief Returns the error code of an error reply
0471      *
0472      * The error code returned is one of the values in the ActionReply::Error
0473      * enumeration if type() == KAuthError, or is totally application-dependent if
0474      * type() == HelperError. It also should be zero for successful replies.
0475      *
0476      * @return The reply error code
0477      */
0478     int error() const;
0480     /**
0481      * @brief Returns the error code of an error reply
0482      *
0483      * The error code returned is one of the values in the ActionReply::Error
0484      * enumeration if type() == KAuthError.
0485      * Result is only valid if the type() == HelperError
0486      *
0487      * @return The reply error code
0488      */
0489     Error errorCode() const;
0491     /**
0492      * @brief Sets the error code of an error reply
0493      *
0494      * If you're setting the error code in the helper because
0495      * you need to return an error to the application, please make sure
0496      * you already have set the type to HelperError, either by calling
0497      * setType() or by creating the reply in the right way.
0498      *
0499      * If the type is Success when you call this method, it will become KAuthError
0500      *
0501      * @param error The new reply error code
0502      */
0503     void setError(int error);
0505     /**
0506      * @brief Sets the error code of an error reply
0507      *
0508      * @see
0509      * If you're setting the error code in the helper, use setError(int)
0510      *
0511      * If the type is Success when you call this method, it will become KAuthError
0512      *
0513      * @param errorCode The new reply error code
0514      */
0515     void setErrorCode(Error errorCode);
0517     /**
0518      * @brief Gets a human-readble description of the error, if available
0519      *
0520      * Currently, replies of type KAuthError rarely report an error description.
0521      * This situation could change in the future.
0522      *
0523      * By now, you can use this method for custom errors of type HelperError.
0524      *
0525      * @return The error human-readable description
0526      */
0527     QString errorDescription() const;
0529     /**
0530      * @brief Sets a human-readble description of the error
0531      *
0532      * Call this method from the helper if you want to send back a description for
0533      * a custom error. Note that this method doesn't affect the errorCode in any way
0534      *
0535      * @param error The new error description
0536      */
0537     void setErrorDescription(const QString &error);
0539     /**
0540      * @brief Serialize the reply into a QByteArray.
0541      *
0542      * This is a convenience method used internally to sent the reply to a remote peer.
0543      * To recreate the reply, use deserialize()
0544      *
0545      * @return A QByteArray representation of this reply
0546      */
0547     QByteArray serialized() const;
0549     /**
0550      * @brief Deserialize a reply from a QByteArray
0551      *
0552      * This method returns a reply from a QByteArray obtained from
0553      * the serialized() method.
0554      *
0555      * @param data A QByteArray obtained with serialized()
0556      */
0557     static ActionReply deserialize(const QByteArray &data);
0559     /// Assignment operator
0560     ActionReply &operator=(const ActionReply &reply);
0562     /**
0563      * @brief Comparison operator
0564      *
0565      * This operator checks if the type and the error code of two replies are the same.
0566      * It <b>doesn't</b> compare the data or the error descriptions, so be careful.
0567      *
0568      * The suggested use is to compare a reply against one of the predefined error replies:
0569      * @code
0570      * if(reply == ActionReply::HelperBusyReply) {
0571      *    // Do something...
0572      * }
0573      * @endcode
0574      *
0575      * Note that you can do it also by compare errorCode() with the relative enumeration value.
0576      */
0577     bool operator==(const ActionReply &reply) const;
0579     /**
0580      * @brief Negated comparison operator
0581      *
0582      * See the operator==() for an important notice.
0583      */
0584     bool operator!=(const ActionReply &reply) const;
0586 private:
0587     QSharedDataPointer<ActionReplyData> d;
0588 };
0590 } // namespace Auth
0592 Q_DECLARE_METATYPE(KAuth::ActionReply)
0594 #endif