File indexing completed on 2024-05-05 17:42:52

0001 /*
0002     SPDX-FileCopyrightText: 2011 Ilia Kats <ilia-kats@gmx.net>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "openconnectauthworkerthread.h"
0008 
0009 #include <QByteArray>
0010 #include <QMutex>
0011 #include <QString>
0012 #include <QWaitCondition>
0013 
0014 #if OPENCONNECT_CHECK_VER(5, 8)
0015 #include <QUrl>
0016 #include <QDesktopServices>
0017 #endif
0018 
0019 extern "C" {
0020 #include <cerrno>
0021 #include <cstdlib>
0022 }
0023 
0024 #include <cstdarg>
0025 
0026 class OpenconnectAuthStaticWrapper
0027 {
0028 public:
0029 #if OPENCONNECT_CHECK_VER(5, 8)
0030     static int openUri(struct openconnect_info *vpninfo, const char *login_uri, void *obj)
0031     {
0032         if (obj)
0033             return static_cast<OpenconnectAuthWorkerThread *>(obj)->openUri(vpninfo, login_uri, obj);
0034         return -1;
0035     }
0036 #endif
0037 #if OPENCONNECT_CHECK_VER(5, 0)
0038     static int writeNewConfig(void *obj, const char *str, int num)
0039     {
0040         if (obj)
0041             return static_cast<OpenconnectAuthWorkerThread *>(obj)->writeNewConfig(str, num);
0042         return -1;
0043     }
0044     static int validatePeerCert(void *obj, const char *str)
0045     {
0046         if (obj)
0047             return static_cast<OpenconnectAuthWorkerThread *>(obj)->validatePeerCert(nullptr, str);
0048         return -1;
0049     }
0050 #else
0051     static int writeNewConfig(void *obj, char *str, int num)
0052     {
0053         if (obj)
0054             return static_cast<OpenconnectAuthWorkerThread *>(obj)->writeNewConfig(str, num);
0055         return -1;
0056     }
0057     static int validatePeerCert(void *obj, OPENCONNECT_X509 *cert, const char *str)
0058     {
0059         if (obj)
0060             return static_cast<OpenconnectAuthWorkerThread *>(obj)->validatePeerCert(cert, str);
0061         return -1;
0062     }
0063 #endif
0064     static int processAuthForm(void *obj, struct oc_auth_form *form)
0065     {
0066         if (obj)
0067             return static_cast<OpenconnectAuthWorkerThread *>(obj)->processAuthFormP(form);
0068         return OC_FORM_RESULT_ERR;
0069     }
0070     static void writeProgress(void *obj, int level, const char *str, ...)
0071     {
0072         if (obj) {
0073             va_list argPtr;
0074             va_start(argPtr, str);
0075             static_cast<OpenconnectAuthWorkerThread *>(obj)->writeProgress(level, str, argPtr);
0076             va_end(argPtr);
0077         }
0078     }
0079 };
0080 
0081 OpenconnectAuthWorkerThread::OpenconnectAuthWorkerThread(QMutex *mutex,
0082                                                          QWaitCondition *waitForUserInput,
0083                                                          bool *userDecidedToQuit,
0084                                                          bool *formGroupChanged,
0085                                                          int cancelFd)
0086     : QThread()
0087     , m_mutex(mutex)
0088     , m_waitForUserInput(waitForUserInput)
0089     , m_userDecidedToQuit(userDecidedToQuit)
0090     , m_formGroupChanged(formGroupChanged)
0091 {
0092     m_openconnectInfo = openconnect_vpninfo_new((char *)"OpenConnect VPN Agent (PlasmaNM - running on KDE)",
0093                                                 OpenconnectAuthStaticWrapper::validatePeerCert,
0094                                                 OpenconnectAuthStaticWrapper::writeNewConfig,
0095                                                 OpenconnectAuthStaticWrapper::processAuthForm,
0096                                                 OpenconnectAuthStaticWrapper::writeProgress,
0097                                                 this);
0098     openconnect_set_cancel_fd(m_openconnectInfo, cancelFd);
0099 #if OPENCONNECT_CHECK_VER(5, 8)
0100     openconnect_set_external_browser_callback(m_openconnectInfo, OpenconnectAuthStaticWrapper::openUri);
0101 #endif
0102 }
0103 
0104 OpenconnectAuthWorkerThread::~OpenconnectAuthWorkerThread()
0105 {
0106     openconnect_vpninfo_free(m_openconnectInfo);
0107 }
0108 
0109 void OpenconnectAuthWorkerThread::run()
0110 {
0111     openconnect_init_ssl();
0112     Q_EMIT initTokens();
0113     int ret = openconnect_obtain_cookie(m_openconnectInfo);
0114     if (*m_userDecidedToQuit) {
0115         return;
0116     }
0117     Q_EMIT cookieObtained(ret);
0118 }
0119 
0120 struct openconnect_info *OpenconnectAuthWorkerThread::getOpenconnectInfo()
0121 {
0122     return m_openconnectInfo;
0123 }
0124 
0125 int OpenconnectAuthWorkerThread::writeNewConfig(const char *buf, int buflen)
0126 {
0127     Q_UNUSED(buflen)
0128     if (*m_userDecidedToQuit) {
0129         return -EINVAL;
0130     }
0131     Q_EMIT writeNewConfig(QString(QByteArray(buf).toBase64()));
0132     return 0;
0133 }
0134 
0135 int OpenconnectAuthWorkerThread::validatePeerCert(void *cert, const char *reason)
0136 {
0137     if (*m_userDecidedToQuit) {
0138         return -EINVAL;
0139     }
0140 
0141 #if OPENCONNECT_CHECK_VER(5, 0)
0142     (void)cert;
0143     const char *fingerprint = openconnect_get_peer_cert_hash(m_openconnectInfo);
0144     char *details = openconnect_get_peer_cert_details(m_openconnectInfo);
0145 #else
0146     char fingerprint[41];
0147     int ret = 0;
0148 
0149     ret = openconnect_get_cert_sha1(m_openconnectInfo, cert, fingerprint);
0150     if (ret) {
0151         return ret;
0152     }
0153 
0154     char *details = openconnect_get_cert_details(m_openconnectInfo, cert);
0155 #endif
0156     bool accepted = false;
0157     m_mutex->lock();
0158     QString qFingerprint(fingerprint);
0159     QString qCertinfo(details);
0160     QString qReason(reason);
0161     Q_EMIT validatePeerCert(qFingerprint, qCertinfo, qReason, &accepted);
0162     m_waitForUserInput->wait(m_mutex);
0163     m_mutex->unlock();
0164     openconnect_free_cert_info(m_openconnectInfo, details);
0165     if (*m_userDecidedToQuit) {
0166         return -EINVAL;
0167     }
0168 
0169     if (accepted) {
0170         return 0;
0171     } else {
0172         return -EINVAL;
0173     }
0174 }
0175 
0176 int OpenconnectAuthWorkerThread::processAuthFormP(struct oc_auth_form *form)
0177 {
0178     if (*m_userDecidedToQuit) {
0179         return -1;
0180     }
0181 
0182     m_mutex->lock();
0183     *m_formGroupChanged = false;
0184     Q_EMIT processAuthForm(form);
0185     m_waitForUserInput->wait(m_mutex);
0186     m_mutex->unlock();
0187     if (*m_userDecidedToQuit) {
0188         return OC_FORM_RESULT_CANCELLED;
0189     }
0190 
0191     if (*m_formGroupChanged) {
0192         return OC_FORM_RESULT_NEWGROUP;
0193     }
0194     return OC_FORM_RESULT_OK;
0195 }
0196 
0197 void OpenconnectAuthWorkerThread::writeProgress(int level, const char *fmt, va_list argPtr)
0198 {
0199     if (*m_userDecidedToQuit) {
0200         return;
0201     }
0202     const QString msg = QString::vasprintf(fmt, argPtr);
0203     Q_EMIT updateLog(msg, level);
0204 }
0205 
0206 #if OPENCONNECT_CHECK_VER(5, 8)
0207 int OpenconnectAuthWorkerThread::openUri(__attribute__((unused)) struct openconnect_info *vpninfo,
0208                                          const char *login_uri, __attribute__((unused)) void *privdata)
0209 {
0210     bool opened = QDesktopServices::openUrl(QUrl(login_uri, QUrl::TolerantMode));
0211     if (!opened) {
0212         OpenconnectAuthStaticWrapper::writeProgress(this, PRG_ERR, "Failed to invoke QDesktopServices::openUrl.");
0213         return 1;
0214     }
0215 
0216     return 0;
0217 }
0218 #endif