File indexing completed on 2024-05-12 05:36:37

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