ECSession.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/platform.h>
  18. #include <chrono>
  19. #include <memory>
  20. #include <mutex>
  21. #include <new>
  22. #include <utility>
  23. #include <cerrno>
  24. #include <cstring>
  25. #include <dirent.h>
  26. #include <poll.h>
  27. #include <pwd.h>
  28. #include <sys/stat.h>
  29. #include <mapidefs.h>
  30. #include <mapitags.h>
  31. #include <kopano/lockhelper.hpp>
  32. #include <kopano/UnixUtil.h>
  33. #include "ECSession.h"
  34. #include "ECSessionManager.h"
  35. #include "ECUserManagement.h"
  36. #include "ECSecurity.h"
  37. #include "ECPluginFactory.h"
  38. #include "SSLUtil.h"
  39. #include <kopano/stringutil.h>
  40. #include "ECDatabase.h"
  41. #include "ECDatabaseUtils.h" // used for PR_INSTANCE_KEY
  42. #include "SOAPUtils.h"
  43. #include "ics.h"
  44. #include "ECICS.h"
  45. #include <kopano/ECIConv.h>
  46. #include "versions.h"
  47. #if defined LINUX || !defined UNICODE
  48. #define WHITESPACE " \t\n\r"
  49. #else
  50. #define WHITESPACE L" \t\n\r"
  51. #endif
  52. namespace KC {
  53. // possible missing SSL function
  54. #ifndef HAVE_EVP_PKEY_CMP
  55. static int EVP_PKEY_cmp(EVP_PKEY *a, EVP_PKEY *b)
  56. {
  57. if (a->type != b->type)
  58. return -1;
  59. if (EVP_PKEY_cmp_parameters(a, b) == 0)
  60. return 0;
  61. switch (a->type)
  62. {
  63. case EVP_PKEY_RSA:
  64. if (BN_cmp(b->pkey.rsa->n,a->pkey.rsa->n) != 0
  65. || BN_cmp(b->pkey.rsa->e,a->pkey.rsa->e) != 0)
  66. return 0;
  67. break;
  68. case EVP_PKEY_DSA:
  69. if (BN_cmp(b->pkey.dsa->pub_key,a->pkey.dsa->pub_key) != 0)
  70. return 0;
  71. break;
  72. case EVP_PKEY_DH:
  73. return -2;
  74. default:
  75. return -2;
  76. }
  77. return 1;
  78. }
  79. #endif
  80. void CreateSessionID(unsigned int ulCapabilities, ECSESSIONID *lpSessionId)
  81. {
  82. ssl_random(!!(ulCapabilities & KOPANO_CAP_LARGE_SESSIONID), lpSessionId);
  83. }
  84. /*
  85. BaseType session
  86. */
  87. BTSession::BTSession(const char *src_addr, ECSESSIONID sessionID,
  88. ECDatabaseFactory *lpDatabaseFactory, ECSessionManager *lpSessionManager,
  89. unsigned int ulCapabilities) :
  90. m_strSourceAddr(src_addr), m_sessionID(sessionID),
  91. m_lpDatabaseFactory(lpDatabaseFactory),
  92. m_lpSessionManager(lpSessionManager),
  93. m_ulClientCapabilities(ulCapabilities)
  94. {
  95. m_ulRefCount = 0;
  96. m_sessionTime = GetProcessTime();
  97. m_ulSessionTimeout = 300;
  98. m_bCheckIP = true;
  99. m_lpUserManagement = NULL;
  100. m_ulRequests = 0;
  101. m_ulLastRequestPort = 0;
  102. }
  103. void BTSession::SetClientMeta(const char *const lpstrClientVersion, const char *const lpstrClientMisc)
  104. {
  105. m_strClientApplicationVersion = lpstrClientVersion ? lpstrClientVersion : "";
  106. m_strClientApplicationMisc = lpstrClientMisc ? lpstrClientMisc : "";
  107. }
  108. void BTSession::GetClientApplicationVersion(std::string *lpstrClientApplicationVersion)
  109. {
  110. lpstrClientApplicationVersion->assign(m_strClientApplicationVersion);
  111. }
  112. void BTSession::GetClientApplicationMisc(std::string *lpstrClientApplicationMisc)
  113. {
  114. scoped_lock lock(m_hRequestStats);
  115. lpstrClientApplicationMisc->assign(m_strClientApplicationMisc);
  116. }
  117. ECRESULT BTSession::Shutdown(unsigned int ulTimeout) {
  118. return erSuccess;
  119. }
  120. ECRESULT BTSession::ValidateOriginator(struct soap *soap)
  121. {
  122. if (!m_bCheckIP)
  123. return erSuccess;
  124. const char *s = ::GetSourceAddr(soap);
  125. if (strcmp(m_strSourceAddr.c_str(), s) == 0)
  126. return erSuccess;
  127. ec_log_err("Denying access to session from source \"%s\" due to unmatched establishing source \"%s\"",
  128. s, m_strSourceAddr.c_str());
  129. return KCERR_END_OF_SESSION;
  130. }
  131. void BTSession::UpdateSessionTime()
  132. {
  133. m_sessionTime = GetProcessTime();
  134. }
  135. ECRESULT BTSession::GetDatabase(ECDatabase **lppDatabase)
  136. {
  137. return GetThreadLocalDatabase(this->m_lpDatabaseFactory, lppDatabase);
  138. }
  139. ECRESULT BTSession::GetAdditionalDatabase(ECDatabase **lppDatabase)
  140. {
  141. std::string str;
  142. return this->m_lpDatabaseFactory->CreateDatabaseObject(lppDatabase, str);
  143. }
  144. ECRESULT BTSession::GetServerGUID(GUID* lpServerGuid){
  145. return m_lpSessionManager->GetServerGUID(lpServerGuid);
  146. }
  147. ECRESULT BTSession::GetNewSourceKey(SOURCEKEY* lpSourceKey){
  148. return m_lpSessionManager->GetNewSourceKey(lpSourceKey);
  149. }
  150. void BTSession::Lock()
  151. {
  152. // Increase our refcount by one
  153. scoped_lock lock(m_hThreadReleasedMutex);
  154. ++this->m_ulRefCount;
  155. }
  156. void BTSession::Unlock()
  157. {
  158. // Decrease our refcount by one, signal ThreadReleased if RefCount == 0
  159. scoped_lock lock(m_hThreadReleasedMutex);
  160. --this->m_ulRefCount;
  161. if(!IsLocked())
  162. m_hThreadReleased.notify_one();
  163. }
  164. time_t BTSession::GetIdleTime()
  165. {
  166. return GetProcessTime() - m_sessionTime;
  167. }
  168. void BTSession::RecordRequest(struct soap* soap)
  169. {
  170. scoped_lock lock(m_hRequestStats);
  171. m_strLastRequestURL = soap->endpoint;
  172. m_ulLastRequestPort = soap->port;
  173. if (soap->proxy_from != nullptr && soap_info(soap)->bProxy)
  174. m_strProxyHost = soap->host;
  175. ++m_ulRequests;
  176. }
  177. unsigned int BTSession::GetRequests()
  178. {
  179. scoped_lock lock(m_hRequestStats);
  180. return m_ulRequests;
  181. }
  182. void BTSession::GetRequestURL(std::string *lpstrClientURL)
  183. {
  184. scoped_lock lock(m_hRequestStats);
  185. lpstrClientURL->assign(m_strLastRequestURL);
  186. }
  187. void BTSession::GetProxyHost(std::string *lpstrProxyHost)
  188. {
  189. scoped_lock lock(m_hRequestStats);
  190. lpstrProxyHost->assign(m_strProxyHost);
  191. }
  192. void BTSession::GetClientPort(unsigned int *lpulPort)
  193. {
  194. scoped_lock lock(m_hRequestStats);
  195. *lpulPort = m_ulLastRequestPort;
  196. }
  197. size_t BTSession::GetInternalObjectSize()
  198. {
  199. scoped_lock lock(m_hRequestStats);
  200. return MEMORY_USAGE_STRING(m_strSourceAddr) +
  201. MEMORY_USAGE_STRING(m_strLastRequestURL) +
  202. MEMORY_USAGE_STRING(m_strProxyHost);
  203. }
  204. ECSession::ECSession(const char *src_addr, ECSESSIONID sessionID,
  205. ECSESSIONGROUPID ecSessionGroupId, ECDatabaseFactory *lpDatabaseFactory,
  206. ECSessionManager *lpSessionManager, unsigned int ulCapabilities,
  207. AUTHMETHOD ulAuthMethod, int pid,
  208. const std::string &cl_ver, const std::string &cl_app,
  209. const std::string &cl_app_ver, const std::string &cl_app_misc) :
  210. BTSession(src_addr, sessionID, lpDatabaseFactory, lpSessionManager,
  211. ulCapabilities),
  212. m_ulAuthMethod(ulAuthMethod), m_ulConnectingPid(pid),
  213. m_ecSessionGroupId(ecSessionGroupId), m_strClientVersion(cl_ver),
  214. m_ulClientVersion(KOPANO_VERSION_UNKNOWN), m_strClientApp(cl_app)
  215. {
  216. m_lpTableManager = new ECTableManager(this);
  217. m_strClientApplicationVersion = cl_app_ver;
  218. m_strClientApplicationMisc = cl_app_misc;
  219. ParseKopanoVersion(cl_ver, &m_ulClientVersion);
  220. // Ignore result.
  221. m_ulSessionTimeout = atoi(lpSessionManager->GetConfig()->GetSetting("session_timeout"));
  222. if (m_ulSessionTimeout < 300)
  223. m_ulSessionTimeout = 300;
  224. m_bCheckIP = strcmp(lpSessionManager->GetConfig()->GetSetting("session_ip_check"), "no") != 0;
  225. // Offline implements its own versions of these objects
  226. m_lpUserManagement = new ECUserManagement(this, m_lpSessionManager->GetPluginFactory(), m_lpSessionManager->GetConfig());
  227. m_lpEcSecurity = new ECSecurity(this, m_lpSessionManager->GetConfig(), m_lpSessionManager->GetAudit());
  228. // Atomically get and AddSession() on the sessiongroup. Needs a ReleaseSession() on the session group to clean up.
  229. m_lpSessionManager->GetSessionGroup(ecSessionGroupId, this, &m_lpSessionGroup);
  230. }
  231. ECSession::~ECSession()
  232. {
  233. Shutdown(0);
  234. /*
  235. * Release our reference to the session group; none of the threads of this session are
  236. * using the object since there are now 0 threads on this session (except this thread)
  237. * Afterwards tell the session manager that the sessiongroup may be an orphan now.
  238. */
  239. if (m_lpSessionGroup) {
  240. m_lpSessionGroup->ReleaseSession(this);
  241. m_lpSessionManager->DeleteIfOrphaned(m_lpSessionGroup);
  242. }
  243. delete m_lpTableManager;
  244. delete m_lpUserManagement;
  245. delete m_lpEcSecurity;
  246. }
  247. /**
  248. * Shut down the session:
  249. *
  250. * - Signal sessiongroup that long-running requests should be cancelled
  251. * - Wait for all users of the session to exit
  252. *
  253. * If the wait takes longer than ulTimeout milliseconds, KCERR_TIMEOUT is
  254. * returned. If this is the case, it is *not* safe to delete the session
  255. *
  256. * @param ulTimeout Timeout in milliseconds
  257. * @result erSuccess or KCERR_TIMEOUT
  258. */
  259. ECRESULT ECSession::Shutdown(unsigned int ulTimeout)
  260. {
  261. ECRESULT er = erSuccess;
  262. /* Shutdown blocking calls for this session on our session group */
  263. if (m_lpSessionGroup != nullptr)
  264. m_lpSessionGroup->ShutdownSession(this);
  265. /* Wait until there are no more running threads using this session */
  266. std::unique_lock<std::mutex> lk(m_hThreadReleasedMutex);
  267. while(IsLocked())
  268. if (m_hThreadReleased.wait_for(lk, std::chrono::milliseconds(ulTimeout)) == std::cv_status::timeout)
  269. break;
  270. lk.unlock();
  271. if (IsLocked())
  272. er = KCERR_TIMEOUT;
  273. return er;
  274. }
  275. ECRESULT ECSession::AddAdvise(unsigned int ulConnection, unsigned int ulKey, unsigned int ulEventMask)
  276. {
  277. ECRESULT hr = erSuccess;
  278. Lock();
  279. if (m_lpSessionGroup)
  280. hr = m_lpSessionGroup->AddAdvise(m_sessionID, ulConnection, ulKey, ulEventMask);
  281. else
  282. hr = KCERR_NOT_INITIALIZED;
  283. Unlock();
  284. return hr;
  285. }
  286. ECRESULT ECSession::AddChangeAdvise(unsigned int ulConnection, notifySyncState *lpSyncState)
  287. {
  288. ECRESULT er = erSuccess;
  289. string strQuery;
  290. ECDatabase* lpDatabase = NULL;
  291. DB_RESULT lpDBResult;
  292. DB_ROW lpDBRow;
  293. ULONG ulChangeId = 0;
  294. Lock();
  295. if (!m_lpSessionGroup) {
  296. er = KCERR_NOT_INITIALIZED;
  297. goto exit;
  298. }
  299. er = m_lpSessionGroup->AddChangeAdvise(m_sessionID, ulConnection, lpSyncState);
  300. if (er != hrSuccess)
  301. goto exit;
  302. er = GetDatabase(&lpDatabase);
  303. if (er != erSuccess)
  304. goto exit;
  305. strQuery = "SELECT c.id FROM changes AS c JOIN syncs AS s "
  306. "ON s.sourcekey=c.parentsourcekey "
  307. "WHERE s.id=" + stringify(lpSyncState->ulSyncId) + " "
  308. "AND c.id>" + stringify(lpSyncState->ulChangeId) + " "
  309. "AND c.sourcesync!=" + stringify(lpSyncState->ulSyncId) + " "
  310. "AND c.change_type >= " + stringify(ICS_MESSAGE) + " "
  311. "AND c.change_type & " + stringify(ICS_MESSAGE) + " != 0 "
  312. "ORDER BY c.id DESC "
  313. "LIMIT 1";
  314. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  315. if (er != hrSuccess)
  316. goto exit;
  317. if (lpDatabase->GetNumRows(lpDBResult) == 0)
  318. goto exit;
  319. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  320. if (lpDBRow == NULL || lpDBRow[0] == NULL) {
  321. er = KCERR_DATABASE_ERROR;
  322. ec_log_err("ECSession::AddChangeAdvise(): row or column null");
  323. goto exit;
  324. }
  325. ulChangeId = strtoul(lpDBRow[0], NULL, 0);
  326. er = m_lpSessionGroup->AddChangeNotification(m_sessionID, ulConnection, lpSyncState->ulSyncId, ulChangeId);
  327. exit:
  328. Unlock();
  329. return er;
  330. }
  331. ECRESULT ECSession::DelAdvise(unsigned int ulConnection)
  332. {
  333. ECRESULT hr = erSuccess;
  334. Lock();
  335. if (m_lpSessionGroup)
  336. hr = m_lpSessionGroup->DelAdvise(m_sessionID, ulConnection);
  337. else
  338. hr = KCERR_NOT_INITIALIZED;
  339. Unlock();
  340. return hr;
  341. }
  342. ECRESULT ECSession::AddNotificationTable(unsigned int ulType, unsigned int ulObjType, unsigned int ulTableId, sObjectTableKey* lpsChildRow, sObjectTableKey* lpsPrevRow, struct propValArray *lpRow)
  343. {
  344. ECRESULT hr = hrSuccess;
  345. Lock();
  346. if (m_lpSessionGroup)
  347. hr = m_lpSessionGroup->AddNotificationTable(m_sessionID, ulType, ulObjType, ulTableId, lpsChildRow, lpsPrevRow, lpRow);
  348. else
  349. hr = KCERR_NOT_INITIALIZED;
  350. Unlock();
  351. return hr;
  352. }
  353. ECRESULT ECSession::GetNotifyItems(struct soap *soap, struct notifyResponse *notifications)
  354. {
  355. ECRESULT hr = erSuccess;
  356. Lock();
  357. if (m_lpSessionGroup)
  358. hr = m_lpSessionGroup->GetNotifyItems(soap, m_sessionID, notifications);
  359. else
  360. hr = KCERR_NOT_INITIALIZED;
  361. Unlock();
  362. return hr;
  363. }
  364. void ECSession::AddBusyState(pthread_t threadId, const char *lpszState,
  365. const struct timespec &threadstart, double start)
  366. {
  367. if (!lpszState) {
  368. ec_log_err("Invalid argument \"lpszState\" in call to ECSession::AddBusyState()");
  369. return;
  370. }
  371. scoped_lock lock(m_hStateLock);
  372. m_mapBusyStates[threadId].fname = lpszState;
  373. m_mapBusyStates[threadId].threadstart = threadstart;
  374. m_mapBusyStates[threadId].start = start;
  375. m_mapBusyStates[threadId].threadid = threadId;
  376. m_mapBusyStates[threadId].state = SESSION_STATE_PROCESSING;
  377. }
  378. void ECSession::UpdateBusyState(pthread_t threadId, int state)
  379. {
  380. scoped_lock lock(m_hStateLock);
  381. auto i = m_mapBusyStates.find(threadId);
  382. if (i != m_mapBusyStates.cend())
  383. i->second.state = state;
  384. else
  385. assert(false);
  386. }
  387. void ECSession::RemoveBusyState(pthread_t threadId)
  388. {
  389. scoped_lock lock(m_hStateLock);
  390. auto i = m_mapBusyStates.find(threadId);
  391. if (i == m_mapBusyStates.cend()) {
  392. assert(false);
  393. return;
  394. }
  395. clockid_t clock;
  396. struct timespec end;
  397. // Since the specified thread is done now, record how much work it has done for us
  398. if(pthread_getcpuclockid(threadId, &clock) == 0) {
  399. clock_gettime(clock, &end);
  400. AddClocks(timespec2dbl(end) - timespec2dbl(i->second.threadstart), 0, GetTimeOfDay() - i->second.start);
  401. } else {
  402. assert(false);
  403. }
  404. m_mapBusyStates.erase(threadId);
  405. }
  406. void ECSession::GetBusyStates(std::list<BUSYSTATE> *lpStates)
  407. {
  408. // this map is very small, since a session only performs one or two functions at a time
  409. // so the lock time is short, which will block _all_ incoming functions
  410. lpStates->clear();
  411. scoped_lock lock(m_hStateLock);
  412. for (const auto &p : m_mapBusyStates)
  413. lpStates->push_back(p.second);
  414. }
  415. void ECSession::AddClocks(double dblUser, double dblSystem, double dblReal)
  416. {
  417. scoped_lock lock(m_hRequestStats);
  418. m_dblUser += dblUser;
  419. m_dblSystem += dblSystem;
  420. m_dblReal += dblReal;
  421. }
  422. void ECSession::GetClocks(double *lpdblUser, double *lpdblSystem, double *lpdblReal)
  423. {
  424. scoped_lock lock(m_hRequestStats);
  425. *lpdblUser = m_dblUser;
  426. *lpdblSystem = m_dblSystem;
  427. *lpdblReal = m_dblReal;
  428. }
  429. void ECSession::GetClientVersion(std::string *lpstrVersion)
  430. {
  431. scoped_lock lock(m_hRequestStats);
  432. lpstrVersion->assign(m_strClientVersion);
  433. }
  434. void ECSession::GetClientApp(std::string *lpstrClientApp)
  435. {
  436. scoped_lock lock(m_hRequestStats);
  437. lpstrClientApp->assign(m_strClientApp);
  438. }
  439. /**
  440. * Get the object id of the object specified by the provided entryid.
  441. * This entryid can either be a short term or 'normal' entryid. If the entryid is a
  442. * short term entryid, the STE manager for this session will be queried for the object id.
  443. * If the entryid is a 'normal' entryid, the cache manager / database will be queried.
  444. *
  445. * @param[in] lpEntryID The entryid to get an object id for.
  446. * @param[out] lpulObjId Pointer to an unsigned int that will be set to the returned object id.
  447. * @param[out] lpbIsShortTerm Optional pointer to a boolean that will be set to true when the entryid
  448. * is a short term entryid.
  449. *
  450. * @retval KCERR_INVALID_PARAMETER lpEntryId or lpulObjId is NULL.
  451. * @retval KCERR_INVALID_ENTRYID The provided entryid is invalid.
  452. * @retval KCERR_NOT_FOUND No object was found for the provided entryid.
  453. */
  454. ECRESULT ECSession::GetObjectFromEntryId(const entryId *lpEntryId, unsigned int *lpulObjId, unsigned int *lpulEidFlags)
  455. {
  456. ECRESULT er;
  457. unsigned int ulObjId = 0;
  458. if (lpEntryId == NULL || lpulObjId == NULL)
  459. return KCERR_INVALID_PARAMETER;
  460. er = m_lpSessionManager->GetCacheManager()->GetObjectFromEntryId(lpEntryId, &ulObjId);
  461. if (er != erSuccess)
  462. return er;
  463. *lpulObjId = ulObjId;
  464. if (lpulEidFlags == NULL)
  465. return erSuccess;
  466. static_assert(offsetof(EID, usFlags) == offsetof(EID_V0, usFlags),
  467. "usFlags member not at same position");
  468. auto d = reinterpret_cast<EID *>(lpEntryId->__ptr);
  469. if (lpEntryId->__size < 0 ||
  470. static_cast<size_t>(lpEntryId->__size) < offsetof(EID, usFlags) + sizeof(d->usFlags)) {
  471. ec_log_err("%s: entryid has size %d; not enough for EID_V1.usFlags",
  472. __func__, lpEntryId->__size);
  473. return MAPI_E_CORRUPT_DATA;
  474. }
  475. *lpulEidFlags = d->usFlags;
  476. return erSuccess;
  477. }
  478. ECRESULT ECSession::LockObject(unsigned int ulObjId)
  479. {
  480. ECRESULT er = erSuccess;
  481. scoped_lock lock(m_hLocksLock);
  482. auto res = m_mapLocks.insert(LockMap::value_type(ulObjId, ECObjectLock()));
  483. if (res.second == true)
  484. er = m_lpSessionManager->GetLockManager()->LockObject(ulObjId, m_sessionID, &res.first->second);
  485. return er;
  486. }
  487. ECRESULT ECSession::UnlockObject(unsigned int ulObjId)
  488. {
  489. ECRESULT er;
  490. scoped_lock lock(m_hLocksLock);
  491. auto i = m_mapLocks.find(ulObjId);
  492. if (i == m_mapLocks.cend())
  493. return erSuccess;
  494. er = i->second.Unlock();
  495. if (er == erSuccess)
  496. m_mapLocks.erase(i);
  497. return er;
  498. }
  499. size_t ECSession::GetObjectSize()
  500. {
  501. size_t ulSize = sizeof(*this);
  502. ulSize += GetInternalObjectSize();
  503. ulSize += MEMORY_USAGE_STRING(m_strClientApp) +
  504. MEMORY_USAGE_STRING(m_strUsername) +
  505. MEMORY_USAGE_STRING(m_strClientVersion);
  506. ulSize += MEMORY_USAGE_MAP(m_mapBusyStates.size(), BusyStateMap);
  507. ulSize += MEMORY_USAGE_MAP(m_mapLocks.size(), LockMap);
  508. if (m_lpEcSecurity)
  509. ulSize += m_lpEcSecurity->GetObjectSize();
  510. // The Table manager size is not callculated here
  511. // ulSize += GetTableManager()->GetObjectSize();
  512. return ulSize;
  513. }
  514. ECAuthSession::ECAuthSession(const char *src_addr, ECSESSIONID sessionID,
  515. ECDatabaseFactory *lpDatabaseFactory, ECSessionManager *lpSessionManager,
  516. unsigned int ulCapabilities) :
  517. BTSession(src_addr, sessionID, lpDatabaseFactory, lpSessionManager,
  518. ulCapabilities)
  519. {
  520. m_ulSessionTimeout = 30; // authenticate within 30 seconds, or else!
  521. m_lpUserManagement = new ECUserManagement(this, m_lpSessionManager->GetPluginFactory(), m_lpSessionManager->GetConfig());
  522. #ifdef HAVE_GSSAPI
  523. m_gssServerCreds = GSS_C_NO_CREDENTIAL;
  524. m_gssContext = GSS_C_NO_CONTEXT;
  525. #endif
  526. }
  527. ECAuthSession::~ECAuthSession()
  528. {
  529. #ifdef HAVE_GSSAPI
  530. OM_uint32 status;
  531. if (m_gssServerCreds)
  532. gss_release_cred(&status, &m_gssServerCreds);
  533. if (m_gssContext)
  534. gss_delete_sec_context(&status, &m_gssContext, GSS_C_NO_BUFFER);
  535. #endif
  536. /* Wait until all locks have been closed */
  537. std::unique_lock<std::mutex> l_thread(m_hThreadReleasedMutex);
  538. m_hThreadReleased.wait(l_thread, [this](void) { return !IsLocked(); });
  539. l_thread.unlock();
  540. if (m_NTLM_pid != -1) {
  541. int status;
  542. // close I/O to make ntlm_auth exit
  543. close(m_stdin);
  544. close(m_stdout);
  545. close(m_stderr);
  546. // wait for process status
  547. waitpid(m_NTLM_pid, &status, 0);
  548. ec_log_info("Removing ntlm_auth on pid %d. Exitstatus: %d", m_NTLM_pid, status);
  549. if (status == -1) {
  550. ec_log_err(string("System call waitpid failed: ") + strerror(errno));
  551. } else {
  552. #ifdef WEXITSTATUS
  553. if(WIFEXITED(status)) { /* Child exited by itself */
  554. if(WEXITSTATUS(status))
  555. ec_log_notice("ntlm_auth exited with non-zero status %d", WEXITSTATUS(status));
  556. } else if(WIFSIGNALED(status)) { /* Child was killed by a signal */
  557. ec_log_err("ntlm_auth was killed by signal %d", WTERMSIG(status));
  558. } else { /* Something strange happened */
  559. ec_log_err("ntlm_auth terminated abnormally");
  560. }
  561. #else
  562. if (status)
  563. ec_log_notice("ntlm_auth exited with status %d", status);
  564. #endif
  565. }
  566. }
  567. delete m_lpUserManagement;
  568. }
  569. ECRESULT ECAuthSession::CreateECSession(ECSESSIONGROUPID ecSessionGroupId,
  570. const std::string &cl_ver, const std::string &cl_app,
  571. const std::string &cl_app_ver, const std::string &cl_app_misc,
  572. ECSESSIONID *sessionID, ECSession **lppNewSession)
  573. {
  574. ECRESULT er = erSuccess;
  575. std::unique_ptr<ECSession> lpSession;
  576. ECSESSIONID newSID;
  577. if (!m_bValidated)
  578. return KCERR_LOGON_FAILED;
  579. CreateSessionID(m_ulClientCapabilities, &newSID);
  580. // ECAuthSessionOffline creates offline version .. no bOverrideClass construction
  581. lpSession.reset(new(std::nothrow) ECSession(m_strSourceAddr.c_str(),
  582. newSID, ecSessionGroupId, m_lpDatabaseFactory,
  583. m_lpSessionManager, m_ulClientCapabilities,
  584. m_ulValidationMethod, m_ulConnectingPid,
  585. cl_ver, cl_app, cl_app_ver, cl_app_misc));
  586. if (lpSession == nullptr)
  587. return KCERR_NOT_ENOUGH_MEMORY;
  588. er = lpSession->GetSecurity()->SetUserContext(m_ulUserID, m_ulImpersonatorID);
  589. if (er != erSuccess)
  590. /* User not found anymore, or error in getting groups. */
  591. return er;
  592. *sessionID = std::move(newSID);
  593. *lppNewSession = lpSession.release();
  594. return erSuccess;
  595. }
  596. // This is a standard user/pass login.
  597. // You always log in as the user you are authenticating with.
  598. ECRESULT ECAuthSession::ValidateUserLogon(const char* lpszName, const char* lpszPassword, const char* lpszImpersonateUser)
  599. {
  600. ECRESULT er;
  601. if (!lpszName)
  602. {
  603. ec_log_err("Invalid argument \"lpszName\" in call to ECAuthSession::ValidateUserLogon()");
  604. return KCERR_INVALID_PARAMETER;
  605. }
  606. if (!lpszPassword) {
  607. ec_log_err("Invalid argument \"lpszPassword\" in call to ECAuthSession::ValidateUserLogon()");
  608. return KCERR_INVALID_PARAMETER;
  609. }
  610. // SYSTEM can't login with user/pass
  611. if (strcasecmp(lpszName, KOPANO_ACCOUNT_SYSTEM) == 0)
  612. return KCERR_NO_ACCESS;
  613. er = m_lpUserManagement->AuthUserAndSync(lpszName, lpszPassword, &m_ulUserID);
  614. if(er != erSuccess)
  615. return er;
  616. er = ProcessImpersonation(lpszImpersonateUser);
  617. if (er != erSuccess)
  618. return er;
  619. m_bValidated = true;
  620. m_ulValidationMethod = METHOD_USERPASSWORD;
  621. return erSuccess;
  622. }
  623. // Validate a user through the socket they are connecting through. This has the special feature
  624. // that you can connect as a different user than you are specifying in the username. For example,
  625. // you could be connecting as 'root' and being granted access because the kopano-server process
  626. // is also running as 'root', but you are actually loggin in as user 'user1'.
  627. ECRESULT ECAuthSession::ValidateUserSocket(int socket, const char* lpszName, const char* lpszImpersonateUser)
  628. {
  629. ECRESULT er = erSuccess;
  630. const char *p = NULL;
  631. bool allowLocalUsers = false;
  632. int pid = 0;
  633. char *ptr = NULL;
  634. char *localAdminUsers = NULL;
  635. if (!lpszName)
  636. {
  637. ec_log_err("Invalid argument \"lpszName\" in call to ECAuthSession::ValidateUserSocket()");
  638. er = KCERR_INVALID_PARAMETER;
  639. goto exit;
  640. }
  641. if (!lpszImpersonateUser) {
  642. ec_log_err("Invalid argument \"lpszImpersonateUser\" in call to ECAuthSession::ValidateUserSocket()");
  643. er = KCERR_INVALID_PARAMETER;
  644. goto exit;
  645. }
  646. p = m_lpSessionManager->GetConfig()->GetSetting("allow_local_users");
  647. if (p != nullptr && strcasecmp(p, "yes") == 0)
  648. allowLocalUsers = true;
  649. // Authentication stage
  650. localAdminUsers = strdup(m_lpSessionManager->GetConfig()->GetSetting("local_admin_users"));
  651. struct passwd pwbuf;
  652. struct passwd *pw;
  653. uid_t uid;
  654. char strbuf[1024];
  655. #ifdef SO_PEERCRED
  656. struct ucred cr;
  657. unsigned int cr_len;
  658. cr_len = sizeof(struct ucred);
  659. if(getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) != 0 || cr_len != sizeof(struct ucred)) {
  660. er = KCERR_LOGON_FAILED;
  661. goto exit;
  662. }
  663. uid = cr.uid; // uid is the uid of the user that is connecting
  664. pid = cr.pid;
  665. #else // SO_PEERCRED
  666. #ifdef HAVE_GETPEEREID
  667. gid_t gid;
  668. if (getpeereid(socket, &uid, &gid)) {
  669. er = KCERR_LOGON_FAILED;
  670. goto exit;
  671. }
  672. #else // HAVE_GETPEEREID
  673. #error I have no way to find out the remote user and I want to cry
  674. #endif // HAVE_GETPEEREID
  675. #endif // SO_PEERCRED
  676. if (geteuid() == uid)
  677. // User connecting is connecting under same UID as the server is running under, allow this
  678. goto userok;
  679. // Lookup user name
  680. pw = NULL;
  681. #ifdef HAVE_GETPWNAM_R
  682. getpwnam_r(lpszName, &pwbuf, strbuf, sizeof(strbuf), &pw);
  683. #else
  684. // OpenBSD does not have getpwnam_r() .. FIXME: threading issue!
  685. pw = getpwnam(lpszName);
  686. #endif
  687. if (allowLocalUsers && pw && pw->pw_uid == uid)
  688. // User connected as himself
  689. goto userok;
  690. p = strtok_r(localAdminUsers, WHITESPACE, &ptr);
  691. while (p) {
  692. pw = NULL;
  693. #ifdef HAVE_GETPWNAM_R
  694. getpwnam_r(p, &pwbuf, strbuf, sizeof(strbuf), &pw);
  695. #else
  696. pw = getpwnam(p);
  697. #endif
  698. if (pw != nullptr && pw->pw_uid == uid)
  699. // A local admin user connected - ok
  700. goto userok;
  701. p = strtok_r(NULL, WHITESPACE, &ptr);
  702. }
  703. er = KCERR_LOGON_FAILED;
  704. goto exit;
  705. userok:
  706. // Check whether user exists in the user database
  707. er = m_lpUserManagement->ResolveObjectAndSync(OBJECTCLASS_USER, lpszName, &m_ulUserID);
  708. if (er != erSuccess)
  709. goto exit;
  710. er = ProcessImpersonation(lpszImpersonateUser);
  711. if (er != erSuccess)
  712. goto exit;
  713. m_bValidated = true;
  714. m_ulValidationMethod = METHOD_SOCKET;
  715. m_ulConnectingPid = pid;
  716. exit:
  717. free(localAdminUsers);
  718. return er;
  719. }
  720. ECRESULT ECAuthSession::ValidateUserCertificate(struct soap* soap, const char* lpszName, const char* lpszImpersonateUser)
  721. {
  722. ECRESULT er = KCERR_LOGON_FAILED;
  723. X509 *cert = NULL; // client certificate
  724. EVP_PKEY *pubkey = NULL; // client public key
  725. EVP_PKEY *storedkey = NULL;
  726. int res = -1;
  727. const char *sslkeys_path = m_lpSessionManager->GetConfig()->GetSetting("sslkeys_path", "", NULL);
  728. std::unique_ptr<DIR, fs_deleter> dh;
  729. if (!soap) {
  730. ec_log_err("Invalid argument \"soap\" in call to ECAuthSession::ValidateUserCertificate()");
  731. er = KCERR_INVALID_PARAMETER;
  732. goto exit;
  733. }
  734. if (!lpszName) {
  735. ec_log_err("Invalid argument \"lpszName\" in call to ECAuthSession::ValidateUserCertificate()");
  736. er = KCERR_INVALID_PARAMETER;
  737. goto exit;
  738. }
  739. if (!lpszImpersonateUser) {
  740. ec_log_err("Invalid argument \"lpszImpersonateUser\" in call to ECAuthSession::ValidateUserCertificate()");
  741. er = KCERR_INVALID_PARAMETER;
  742. goto exit;
  743. }
  744. if (!sslkeys_path || sslkeys_path[0] == '\0') {
  745. ec_log_warn("No public keys directory defined in sslkeys_path.");
  746. goto exit;
  747. }
  748. cert = SSL_get_peer_certificate(soap->ssl);
  749. if (!cert) {
  750. // Windows client without SSL certificate
  751. ec_log_info("No certificate in SSL connection.");
  752. goto exit;
  753. }
  754. pubkey = X509_get_pubkey(cert); // need to free
  755. if (!pubkey) {
  756. // if you get here, please tell me how, 'cause I'd like to know :)
  757. ec_log_info("No public key in certificate.");
  758. goto exit;
  759. }
  760. dh.reset(opendir(sslkeys_path));
  761. if (dh == nullptr) {
  762. ec_log_info("Cannot read directory \"%s\": %s", sslkeys_path, strerror(errno));
  763. er = KCERR_LOGON_FAILED;
  764. goto exit;
  765. }
  766. for (const struct dirent *dentry = readdir(dh.get());
  767. dentry != nullptr; dentry = readdir(dh.get())) {
  768. const char *bname = dentry->d_name;
  769. auto fullpath = std::string(sslkeys_path) + "/" + bname;
  770. struct stat sb;
  771. if (stat(fullpath.c_str(), &sb) < 0 || !S_ISREG(sb.st_mode))
  772. continue;
  773. auto biofile = BIO_new_file(fullpath.c_str(), "r");
  774. if (!biofile) {
  775. ec_log_info("Unable to create BIO for \"%s\": %s", bname, ERR_error_string(ERR_get_error(), NULL));
  776. continue;
  777. }
  778. storedkey = PEM_read_bio_PUBKEY(biofile, NULL, NULL, NULL);
  779. if (!storedkey) {
  780. ec_log_info("Unable to read PUBKEY from \"%s\": %s", bname, ERR_error_string(ERR_get_error(), NULL));
  781. BIO_free(biofile);
  782. continue;
  783. }
  784. res = EVP_PKEY_cmp(pubkey, storedkey);
  785. BIO_free(biofile);
  786. EVP_PKEY_free(storedkey);
  787. if (res <= 0) {
  788. ec_log_info("Certificate \"%s\" does not match.", bname);
  789. } else {
  790. er = erSuccess;
  791. ec_log_info("Accepted certificate \"%s\" from client.", bname);
  792. break;
  793. }
  794. }
  795. if (er != erSuccess)
  796. goto exit;
  797. // Check whether user exists in the user database
  798. er = m_lpUserManagement->ResolveObjectAndSync(OBJECTCLASS_USER, lpszName, &m_ulUserID);
  799. if (er != erSuccess)
  800. goto exit;
  801. er = ProcessImpersonation(lpszImpersonateUser);
  802. if (er != erSuccess)
  803. goto exit;
  804. m_bValidated = true;
  805. m_ulValidationMethod = METHOD_SSL_CERT;
  806. exit:
  807. if (cert)
  808. X509_free(cert);
  809. if (pubkey)
  810. EVP_PKEY_free(pubkey);
  811. return er;
  812. }
  813. #define NTLMBUFFER 8192
  814. ECRESULT ECAuthSession::ValidateSSOData(struct soap* soap, const char* lpszName, const char* lpszImpersonateUser, const char* szClientVersion, const char *szClientApp, const char *szClientAppVersion, const char *szClientAppMisc, const struct xsd__base64Binary* lpInput, struct xsd__base64Binary **lppOutput)
  815. {
  816. ECRESULT er = KCERR_INVALID_PARAMETER;
  817. if (!soap) {
  818. ec_log_err("Invalid argument \"soap\" in call to ECAuthSession::ValidateSSOData()");
  819. return er;
  820. }
  821. if (!lpszName) {
  822. ec_log_err("Invalid argument \"lpszName\" in call to ECAuthSession::ValidateSSOData()");
  823. return er;
  824. }
  825. if (!lpszImpersonateUser) {
  826. ec_log_err("Invalid argument \"lpszImpersonateUser\" in call to ECAuthSession::ValidateSSOData()");
  827. return er;
  828. }
  829. if (!szClientVersion) {
  830. ec_log_err("Invalid argument \"szClientVersion\" in call to ECAuthSession::ValidateSSOData()");
  831. return er;
  832. }
  833. if (!szClientApp) {
  834. ec_log_err("Invalid argument \"szClientApp\" in call to ECAuthSession::ValidateSSOData()");
  835. return er;
  836. }
  837. if (!lpInput) {
  838. ec_log_err("Invalid argument \"lpInput\" in call to ECAuthSession::ValidateSSOData()");
  839. return er;
  840. }
  841. if (!lppOutput) {
  842. ec_log_err("Invalid argument \"lppOutput\" in call to ECAuthSession::ValidateSSOData()");
  843. return er;
  844. }
  845. er = KCERR_LOGON_FAILED;
  846. // first NTLM package starts with that signature, continues are detected by the filedescriptor
  847. if (m_NTLM_pid != -1 || strncmp((const char*)lpInput->__ptr, "NTLM", 4) == 0)
  848. er = ValidateSSOData_NTLM(soap, lpszName, szClientVersion, szClientApp, szClientAppVersion, szClientAppMisc, lpInput, lppOutput);
  849. else
  850. er = ValidateSSOData_KRB5(soap, lpszName, szClientVersion, szClientApp, szClientAppVersion, szClientAppMisc, lpInput, lppOutput);
  851. if (er != erSuccess)
  852. return er;
  853. er = ProcessImpersonation(lpszImpersonateUser);
  854. if (er != erSuccess)
  855. return er;
  856. return erSuccess;
  857. }
  858. #ifdef HAVE_GSSAPI
  859. const char gss_display_status_fail_message[] = "Call to gss_display_status failed. Reason: ";
  860. static ECRESULT LogKRB5Error_2(const char *msg, OM_uint32 code, OM_uint32 type)
  861. {
  862. gss_buffer_desc gssMessage = GSS_C_EMPTY_BUFFER;
  863. OM_uint32 status = 0;
  864. OM_uint32 context = 0;
  865. if (msg == NULL) {
  866. ec_log_err("Invalid argument \"msg\" in call to ECAuthSession::LogKRB5Error()");
  867. return KCERR_INVALID_PARAMETER;
  868. }
  869. ECRESULT retval = KCERR_CALL_FAILED;
  870. do {
  871. OM_uint32 result = gss_display_status(&status, code, type, GSS_C_NULL_OID, &context, &gssMessage);
  872. switch (result) {
  873. case GSS_S_COMPLETE:
  874. ec_log_warn("%s: %s", msg, (char*)gssMessage.value);
  875. retval = erSuccess;
  876. break;
  877. case GSS_S_BAD_MECH:
  878. ec_log_warn("%s: %s", gss_display_status_fail_message, "unsupported mechanism type was requested.");
  879. retval = KCERR_CALL_FAILED;
  880. break;
  881. case GSS_S_BAD_STATUS:
  882. ec_log_warn("%s: %s", gss_display_status_fail_message, "status value was not recognized, or the status type was neither GSS_C_GSS_CODE nor GSS_C_MECH_CODE.");
  883. retval = KCERR_CALL_FAILED;
  884. break;
  885. }
  886. gss_release_buffer(&status, &gssMessage);
  887. } while (context != 0);
  888. return retval;
  889. }
  890. ECRESULT ECAuthSession::LogKRB5Error(const char* msg, OM_uint32 major, OM_uint32 minor)
  891. {
  892. if (!msg) {
  893. ec_log_err("Invalid argument \"msg\" in call to ECAuthSession::LogKRB5Error()");
  894. return KCERR_INVALID_PARAMETER;
  895. }
  896. LogKRB5Error_2(msg, major, GSS_C_GSS_CODE);
  897. return LogKRB5Error_2(msg, minor, GSS_C_MECH_CODE);
  898. }
  899. #endif
  900. ECRESULT ECAuthSession::ValidateSSOData_KRB5(struct soap* soap, const char* lpszName, const char* szClientVersion, const char* szClientApp, const char *szClientAppVersion, const char *szClientAppMisc, const struct xsd__base64Binary* lpInput, struct xsd__base64Binary** lppOutput)
  901. {
  902. ECRESULT er = KCERR_INVALID_PARAMETER;
  903. #ifndef HAVE_GSSAPI
  904. ec_log_err("Incoming Kerberos request, but this server was build without GSSAPI support.");
  905. #else
  906. OM_uint32 retval, status;
  907. gss_name_t gssServername = GSS_C_NO_NAME;
  908. gss_buffer_desc gssInputBuffer = GSS_C_EMPTY_BUFFER;
  909. const char *szHostname = NULL;
  910. std::string principal;
  911. gss_name_t gssUsername = GSS_C_NO_NAME;
  912. gss_buffer_desc gssUserBuffer = GSS_C_EMPTY_BUFFER;
  913. gss_buffer_desc gssOutputToken = GSS_C_EMPTY_BUFFER;
  914. std::string strUsername;
  915. string::size_type pos;
  916. struct xsd__base64Binary *lpOutput = NULL;
  917. if (!soap) {
  918. ec_log_err("Invalid argument \"soap\" in call to ECAuthSession::ValidateSSOData_KRB5()");
  919. goto exit;
  920. }
  921. if (!lpszName) {
  922. ec_log_err("Invalid argument \"lpszName\" in call to ECAuthSession::ValidateSSOData_KRB5()");
  923. goto exit;
  924. }
  925. if (!szClientVersion) {
  926. ec_log_err("Invalid argument \"zClientVersionin\" in call to ECAuthSession::ValidateSSOData_KRB5()");
  927. goto exit;
  928. }
  929. if (!szClientApp) {
  930. ec_log_err("Invalid argument \"szClientApp\" in call to ECAuthSession::ValidateSSOData_KRB5()");
  931. goto exit;
  932. }
  933. if (!lpInput) {
  934. ec_log_err("Invalid argument \"lpInput\" in call to ECAuthSession::ValidateSSOData_KRB5()");
  935. goto exit;
  936. }
  937. if (!lppOutput) {
  938. ec_log_err("Invalid argument \"lppOutput\" in call to ECAuthSession::ValidateSSOData_KRB5()");
  939. goto exit;
  940. }
  941. er = KCERR_LOGON_FAILED;
  942. if (m_gssServerCreds == GSS_C_NO_CREDENTIAL) {
  943. m_gssContext = GSS_C_NO_CONTEXT;
  944. // ECServer made sure this setting option always contains the best hostname
  945. // If it's not there, that's unacceptable.
  946. szHostname = m_lpSessionManager->GetConfig()->GetSetting("server_hostname");
  947. if (!szHostname || szHostname[0] == '\0') {
  948. ec_log_crit("Hostname not found, required for Kerberos");
  949. goto exit;
  950. }
  951. principal = "kopano@";
  952. principal += szHostname;
  953. ec_log_debug("Kerberos principal: %s", principal.c_str());
  954. gssInputBuffer.value = (void*)principal.data();
  955. gssInputBuffer.length = principal.length() + 1;
  956. retval = gss_import_name(&status, &gssInputBuffer, GSS_C_NT_HOSTBASED_SERVICE, &gssServername);
  957. if (retval != GSS_S_COMPLETE) {
  958. LogKRB5Error("Unable to import server name", retval, status);
  959. goto exit;
  960. }
  961. retval = gss_acquire_cred(&status, gssServername, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &m_gssServerCreds, NULL, NULL);
  962. if (retval != GSS_S_COMPLETE) {
  963. LogKRB5Error("Unable to acquire credentials handle", retval, status);
  964. goto exit;
  965. }
  966. }
  967. gssInputBuffer.length = lpInput->__size;
  968. gssInputBuffer.value = lpInput->__ptr;
  969. retval = gss_accept_sec_context(&status, &m_gssContext, m_gssServerCreds, &gssInputBuffer, GSS_C_NO_CHANNEL_BINDINGS, &gssUsername, NULL, &gssOutputToken, NULL, NULL, NULL);
  970. if (gssOutputToken.length) {
  971. // we need to send data back to the client, no need to consider retval
  972. lpOutput = s_alloc<struct xsd__base64Binary>(soap);
  973. lpOutput->__size = gssOutputToken.length;
  974. lpOutput->__ptr = s_alloc<unsigned char>(soap, gssOutputToken.length);
  975. memcpy(lpOutput->__ptr, gssOutputToken.value, gssOutputToken.length);
  976. gss_release_buffer(&status, &gssOutputToken);
  977. }
  978. if (retval == GSS_S_CONTINUE_NEEDED) {
  979. er = KCERR_SSO_CONTINUE;
  980. goto exit;
  981. } else if (retval != GSS_S_COMPLETE) {
  982. LogKRB5Error("Unable to accept security context", retval, status);
  983. ZLOG_AUDIT(m_lpSessionManager->GetAudit(), "authenticate failed user='%s' from='%s' method='kerberos sso' program='%s'",
  984. lpszName, soap->host, szClientApp);
  985. goto exit;
  986. }
  987. retval = gss_display_name(&status, gssUsername, &gssUserBuffer, NULL);
  988. if (retval) {
  989. LogKRB5Error("Unable to convert username", retval, status);
  990. goto exit;
  991. }
  992. ec_log_debug("Kerberos username: %s", static_cast<const char *>(gssUserBuffer.value));
  993. // kerberos returns: username@REALM, username is case-insensitive
  994. strUsername.assign((char*)gssUserBuffer.value, gssUserBuffer.length);
  995. pos = strUsername.find_first_of('@');
  996. if (pos != string::npos)
  997. strUsername.erase(pos);
  998. if (strcasecmp(strUsername.c_str(), lpszName) == 0) {
  999. er = m_lpUserManagement->ResolveObjectAndSync(ACTIVE_USER, lpszName, &m_ulUserID);
  1000. // don't check NONACTIVE, since those shouldn't be able to login
  1001. if(er != erSuccess)
  1002. goto exit;
  1003. m_bValidated = true;
  1004. m_ulValidationMethod = METHOD_SSO;
  1005. ec_log_info("Kerberos Single Sign-On: User \"%s\" authenticated", lpszName);
  1006. ZLOG_AUDIT(m_lpSessionManager->GetAudit(), "authenticate ok user='%s' from='%s' method='kerberos sso' program='%s'",
  1007. lpszName, soap->host, szClientApp);
  1008. } else {
  1009. ec_log_err("Kerberos username \"%s\" authenticated, but user \"%s\" requested.", (char*)gssUserBuffer.value, lpszName);
  1010. ZLOG_AUDIT(m_lpSessionManager->GetAudit(), "authenticate spoofed user='%s' requested='%s' from='%s' method='kerberos sso' program='%s'",
  1011. static_cast<char *>(gssUserBuffer.value), lpszName, soap->host, szClientApp);
  1012. }
  1013. exit:
  1014. if (gssUserBuffer.length)
  1015. gss_release_buffer(&status, &gssUserBuffer);
  1016. if (gssOutputToken.length)
  1017. gss_release_buffer(&status, &gssOutputToken);
  1018. if (gssUsername != GSS_C_NO_NAME)
  1019. gss_release_name(&status, &gssUsername);
  1020. if (gssServername != GSS_C_NO_NAME)
  1021. gss_release_name(&status, &gssServername);
  1022. if (lppOutput != nullptr)
  1023. *lppOutput = lpOutput;
  1024. #endif
  1025. return er;
  1026. }
  1027. ECRESULT ECAuthSession::ValidateSSOData_NTLM(struct soap* soap, const char* lpszName, const char* szClientVersion, const char* szClientApp, const char *szClientAppVersion, const char *szClientAppMisc, const struct xsd__base64Binary* lpInput, struct xsd__base64Binary **lppOutput)
  1028. {
  1029. ECRESULT er = KCERR_INVALID_PARAMETER;
  1030. struct xsd__base64Binary *lpOutput = NULL;
  1031. char buffer[NTLMBUFFER];
  1032. std::string strEncoded, strDecoded, strAnswer;
  1033. ssize_t bytes = 0;
  1034. char separator = '\\'; // get config version
  1035. struct pollfd pollfd[2] = {{m_stdout, POLLIN | POLLRDHUP}, {m_stderr, POLLIN | POLLRDHUP}};
  1036. if (!soap) {
  1037. ec_log_err("Invalid argument \"soap\" in call to ECAuthSession::ValidateSSOData_NTLM()");
  1038. return er;
  1039. }
  1040. if (!lpszName) {
  1041. ec_log_err("Invalid argument \"lpszName\" in call to ECAuthSession::ValidateSSOData_NTLM()");
  1042. return er;
  1043. }
  1044. if (!szClientVersion) {
  1045. ec_log_err("Invalid argument \"zClientVersionin\" in call to ECAuthSession::ValidateSSOData_NTLM()");
  1046. return er;
  1047. }
  1048. if (!szClientApp) {
  1049. ec_log_err("Invalid argument \"szClientApp\" in call to ECAuthSession::ValidateSSOData_NTLM()");
  1050. return er;
  1051. }
  1052. if (!lpInput) {
  1053. ec_log_err("Invalid argument \"lpInput\" in call to ECAuthSession::ValidateSSOData_NTLM()");
  1054. return er;
  1055. }
  1056. if (!lppOutput) {
  1057. ec_log_err("Invalid argument \"lppOutput\" in call to ECAuthSession::ValidateSSOData_NTLM()");
  1058. return er;
  1059. }
  1060. er = KCERR_LOGON_FAILED;
  1061. strEncoded = base64_encode(lpInput->__ptr, lpInput->__size);
  1062. errno = 0;
  1063. if (m_NTLM_pid == -1) {
  1064. // start new ntlmauth pipe
  1065. // TODO: configurable path?
  1066. if (pipe(m_NTLM_stdin) == -1 || pipe(m_NTLM_stdout) == -1 || pipe(m_NTLM_stderr) == -1) {
  1067. ec_log_crit(string("Unable to create communication pipes for ntlm_auth: ") + strerror(errno));
  1068. return er;
  1069. }
  1070. /*
  1071. * Why are we using vfork() ?
  1072. *
  1073. * You might as well use fork() here but vfork() is much faster in our case; this is because vfork() doesn't actually duplicate
  1074. * any pages, expecting you to call execl(). Watch out though, since data changes done in the client process before execl() WILL
  1075. * affect the mother process. (however, the file descriptor table is correctly cloned)
  1076. *
  1077. * The reason fork() is slow is that even though it is doing a Copy-On-Write copy, it still needs to do some page-copying to set up your
  1078. * process. This copying time increases with memory usage of the mother process; in fact, running 200 forks() on a process occupying
  1079. * 512MB of memory takes 15 seconds, while the same vfork()/exec() loop takes under .5 of a second.
  1080. *
  1081. * If vfork() is not available, or is broken on another platform, it is safe to simply replace it with fork(), but it will be quite slow!
  1082. */
  1083. m_NTLM_pid = vfork();
  1084. if (m_NTLM_pid == -1) {
  1085. // broken
  1086. ec_log_crit(string("Unable to start new process for ntlm_auth: ") + strerror(errno));
  1087. return er;
  1088. } else if (m_NTLM_pid == 0) {
  1089. // client
  1090. int j, k;
  1091. close(m_NTLM_stdin[1]);
  1092. close(m_NTLM_stdout[0]);
  1093. close(m_NTLM_stderr[0]);
  1094. dup2(m_NTLM_stdin[0], 0);
  1095. dup2(m_NTLM_stdout[1], 1);
  1096. dup2(m_NTLM_stderr[1], 2);
  1097. // close all other open file descriptors, so ntlm doesn't keep the kopano-server sockets open
  1098. j = getdtablesize();
  1099. for (k = 3; k < j; ++k)
  1100. close(k);
  1101. execl("/bin/sh", "sh", "-c", "ntlm_auth -d0 --helper-protocol=squid-2.5-ntlmssp", NULL);
  1102. ec_log_crit(string("Cannot start ntlm_auth: ") + strerror(errno));
  1103. _exit(2);
  1104. } else {
  1105. // parent
  1106. ec_log_info("New ntlm_auth started on pid %d", m_NTLM_pid);
  1107. close(m_NTLM_stdin[0]);
  1108. close(m_NTLM_stdout[1]);
  1109. close(m_NTLM_stderr[1]);
  1110. m_stdin = ec_relocate_fd(m_NTLM_stdin[1]);
  1111. m_stdout = ec_relocate_fd(m_NTLM_stdout[0]);
  1112. m_stderr = ec_relocate_fd(m_NTLM_stderr[0]);
  1113. // Yo! Refresh!
  1114. write(m_stdin, "YR ", 3);
  1115. write(m_stdin, strEncoded.c_str(), strEncoded.length());
  1116. write(m_stdin, "\n", 1);
  1117. }
  1118. } else {
  1119. // Knock knock! who's there?
  1120. write(m_stdin, "KK ", 3);
  1121. write(m_stdin, strEncoded.c_str(), strEncoded.length());
  1122. write(m_stdin, "\n", 1);
  1123. }
  1124. memset(buffer, 0, NTLMBUFFER);
  1125. retry:
  1126. pollfd[0].revents = pollfd[1].revents = 0;
  1127. int ret = poll(pollfd, 2, 10 * 1000); // timeout of 10 seconds before ntlm_auth can respond too large?
  1128. if (ret < 0) {
  1129. if (errno == EINTR)
  1130. goto retry;
  1131. ec_log_err(string("Error while waiting for data from ntlm_auth: ") + strerror(errno));
  1132. return er;
  1133. }
  1134. if (ret == 0) {
  1135. // timeout
  1136. ec_log_err("Timeout while reading from ntlm_auth");
  1137. return er;
  1138. }
  1139. // stderr is optional, and always written first
  1140. if (pollfd[1].revents & (POLLIN | POLLRDHUP)) {
  1141. // log stderr of ntlm_auth to logfile (loop?)
  1142. bytes = read(m_stderr, buffer, NTLMBUFFER-1);
  1143. if (bytes >= 0)
  1144. buffer[bytes] = '\0';
  1145. // print in lower level. if ntlm_auth was not installed (kerberos only environment), you won't care that ntlm_auth doesn't work.
  1146. // login error is returned to the client, which was expected anyway.
  1147. ec_log_notice(string("Received error from ntlm_auth:\n") + buffer);
  1148. return er;
  1149. }
  1150. // stdout is mandatory, so always read from this pipe
  1151. memset(buffer, 0, NTLMBUFFER);
  1152. bytes = read(m_stdout, buffer, NTLMBUFFER-1);
  1153. if (bytes < 0) {
  1154. ec_log_err(string("Unable to read data from ntlm_auth: ") + strerror(errno));
  1155. return er;
  1156. } else if (bytes == 0) {
  1157. ec_log_err("Nothing read from ntlm_auth");
  1158. return er;
  1159. }
  1160. if (buffer[bytes-1] == '\n')
  1161. /*
  1162. * Strip newline right away, it is not useful for logging,
  1163. * nor for base64_decode.
  1164. */
  1165. buffer[--bytes] = '\0';
  1166. if (bytes < 2) {
  1167. /* Ensure buffer[0]==.. && buffer[1]==.. is valid to do */
  1168. ec_log_err("Short reply from ntlm_auth");
  1169. return er;
  1170. }
  1171. if (bytes >= 3)
  1172. /*
  1173. * Extract response text (if any) following the reply code
  1174. * (and space). Else left empty.
  1175. */
  1176. strAnswer.assign(buffer + 3, bytes - 3);
  1177. if (buffer[0] == 'B' && buffer[1] == 'H') {
  1178. // Broken Helper
  1179. ec_log_err("Incorrect data fed to ntlm_auth");
  1180. return er;
  1181. } else if (buffer[0] == 'T' && buffer[1] == 'T') {
  1182. // Try This
  1183. strDecoded = base64_decode(strAnswer);
  1184. lpOutput = s_alloc<struct xsd__base64Binary>(soap);
  1185. lpOutput->__size = strDecoded.length();
  1186. lpOutput->__ptr = s_alloc<unsigned char>(soap, strDecoded.length());
  1187. memcpy(lpOutput->__ptr, strDecoded.data(), strDecoded.length());
  1188. er = KCERR_SSO_CONTINUE;
  1189. } else if (buffer[0] == 'A' && buffer[1] == 'F') {
  1190. // Authentication Fine
  1191. // Samba default runs in UTF-8 and setting 'unix charset' to windows-1252 in the samba config will break ntlm_auth
  1192. // convert the username before we use it in Kopano
  1193. ECIConv iconv("windows-1252", "utf-8");
  1194. if (!iconv.canConvert()) {
  1195. ec_log_crit("Problem setting up windows-1252 to utf-8 converter");
  1196. return er;
  1197. }
  1198. strAnswer = iconv.convert(strAnswer);
  1199. ec_log_info("Found username (%s)", strAnswer.c_str());
  1200. // if the domain separator is not found, assume we only have the username (samba)
  1201. string::size_type pos = strAnswer.find_first_of(separator);
  1202. if (pos != string::npos) {
  1203. ++pos;
  1204. strAnswer.assign(strAnswer, pos, strAnswer.length()-pos);
  1205. }
  1206. // Check whether user exists in the user database
  1207. er = m_lpUserManagement->ResolveObjectAndSync(ACTIVE_USER, (char *)strAnswer.c_str(), &m_ulUserID);
  1208. // don't check NONACTIVE, since those shouldn't be able to login
  1209. if(er != erSuccess)
  1210. return er;
  1211. if (strcasecmp(lpszName, strAnswer.c_str()) != 0) {
  1212. // cannot open another user without password
  1213. // or should we check permissions ?
  1214. ec_log_warn("Single Sign-On: User \"%s\" authenticated, but user \"%s\" requested.", strAnswer.c_str(), lpszName);
  1215. ZLOG_AUDIT(m_lpSessionManager->GetAudit(), "authenticate spoofed user='%s' requested='%s' from='%s' method='ntlm sso' program='%s'",
  1216. strAnswer.c_str(), lpszName, soap->host, szClientApp);
  1217. er = KCERR_LOGON_FAILED;
  1218. } else {
  1219. m_bValidated = true;
  1220. m_ulValidationMethod = METHOD_SSO;
  1221. er = erSuccess;
  1222. ec_log_info("Single Sign-On: User \"%s\" authenticated", strAnswer.c_str());
  1223. ZLOG_AUDIT(m_lpSessionManager->GetAudit(), "authenticate ok user='%s' from='%s' method='ntlm sso' program='%s'",
  1224. lpszName, soap->host, szClientApp);
  1225. }
  1226. } else if (buffer[0] == 'N' && buffer[1] == 'A') {
  1227. // Not Authenticated
  1228. ec_log_info("Requested user \"%s\" denied. Not authenticated: \"%s\"", lpszName, strAnswer.c_str());
  1229. ZLOG_AUDIT(m_lpSessionManager->GetAudit(), "authenticate failed user='%s' from='%s' method='ntlm sso' program='%s'",
  1230. lpszName, soap->host, szClientApp);
  1231. er = KCERR_LOGON_FAILED;
  1232. } else {
  1233. // unknown response?
  1234. ec_log_err("Unknown response from ntlm_auth: %.*s", static_cast<int>(bytes), buffer);
  1235. return KCERR_CALL_FAILED;
  1236. }
  1237. *lppOutput = lpOutput;
  1238. return er;
  1239. }
  1240. #undef NTLMBUFFER
  1241. ECRESULT ECAuthSession::ProcessImpersonation(const char* lpszImpersonateUser)
  1242. {
  1243. if (lpszImpersonateUser == NULL || *lpszImpersonateUser == '\0') {
  1244. m_ulImpersonatorID = EC_NO_IMPERSONATOR;
  1245. return erSuccess;
  1246. }
  1247. m_ulImpersonatorID = m_ulUserID;
  1248. return m_lpUserManagement->ResolveObjectAndSync(OBJECTCLASS_USER,
  1249. lpszImpersonateUser, &m_ulUserID);
  1250. }
  1251. size_t ECAuthSession::GetObjectSize()
  1252. {
  1253. size_t ulSize = sizeof(*this);
  1254. return ulSize;
  1255. }
  1256. } /* namespace */