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"