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