ECSoapServerConnection.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  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 <ctime>
  19. #include <kopano/ECLogger.h>
  20. #ifdef HAVE_SYS_STAT_H
  21. #include <sys/stat.h>
  22. #endif
  23. #ifdef HAVE_SYS_UN_H
  24. #include <sys/un.h>
  25. #endif
  26. #include "ECSoapServerConnection.h"
  27. #include "ECServerEntrypoint.h"
  28. #include "ECClientUpdate.h"
  29. # include <dirent.h>
  30. # include <fcntl.h>
  31. # include <unistd.h>
  32. # include <kopano/UnixUtil.h>
  33. struct soap_connection_thread {
  34. ECSoapServerConnection* lpSoapConnClass;
  35. struct soap* lpSoap;
  36. };
  37. /**
  38. * Creates a AF_UNIX socket in a given location and starts to listen
  39. * on that socket.
  40. *
  41. * @param unix_socket the file location of that socket
  42. * @param lpLogger a logger object
  43. * @param bInit unused
  44. * @param mode change the mode of the file to this value (octal!)
  45. *
  46. * @return the socket we're listening on, or -1 for failure.
  47. */
  48. static int create_pipe_socket(const char *unix_socket, ECConfig *lpConfig,
  49. bool bInit, int mode)
  50. {
  51. int s;
  52. int er = 0;
  53. struct sockaddr_un saddr;
  54. memset(&saddr, 0, sizeof(struct sockaddr_un));
  55. if (strlen(unix_socket) >= sizeof(saddr.sun_path)) {
  56. ec_log_err("UNIX domain socket path \"%s\" is too long", unix_socket);
  57. return -1;
  58. }
  59. s = socket(PF_UNIX, SOCK_STREAM, 0);
  60. if(s < 0) {
  61. ec_log_crit("Unable to create AF_UNIX socket: %s", strerror(errno));
  62. return -1;
  63. }
  64. memset(&saddr,0,sizeof(saddr));
  65. saddr.sun_family = AF_UNIX;
  66. kc_strlcpy(saddr.sun_path, unix_socket, sizeof(saddr.sun_path));
  67. unlink(unix_socket);
  68. if (bind(s, (struct sockaddr*)&saddr, 2 + strlen(unix_socket)) == -1) {
  69. ec_log_crit("Unable to bind to socket %s: %s. This is usually caused by another process (most likely another server) already using this port. This program will terminate now.", unix_socket, strerror(errno));
  70. kill(0, SIGTERM);
  71. exit(1);
  72. }
  73. er = chmod(unix_socket,mode);
  74. if(er) {
  75. ec_log_crit("Unable to chmod socket %s. Error: %s", unix_socket, strerror(errno));
  76. close(s);
  77. return -1;
  78. }
  79. if(er) {
  80. ec_log_crit("Unable to chown socket %s, to %s:%s. Error: %s", unix_socket, lpConfig->GetSetting("run_as_user"), lpConfig->GetSetting("run_as_group"), strerror(errno));
  81. close(s);
  82. return -1;
  83. }
  84. if (listen(s, SOMAXCONN) == -1) {
  85. ec_log_crit("Can't listen on unix socket %s", unix_socket);
  86. close(s);
  87. return -1;
  88. }
  89. return s;
  90. }
  91. /*
  92. * Handles the HTTP GET command from soap, only the client update install may be downloaded.
  93. *
  94. * This function can only be called when client_update_enabled is set to yes.
  95. */
  96. static int http_get(struct soap *soap)
  97. {
  98. int nRet = 404;
  99. if (soap == NULL)
  100. goto exit;
  101. if (strncmp(soap->path, "/autoupdate", strlen("/autoupdate")) == 0) {
  102. ec_log_debug("Client update request '%s'.", soap->path);
  103. nRet = HandleClientUpdate(soap);
  104. } else {
  105. ec_log_debug("Unrecognized GET url '%s'.", soap->path);
  106. }
  107. exit:
  108. soap_end_send(soap);
  109. return nRet;
  110. }
  111. int kc_ssl_options(struct soap *soap, char *protos, const char *ciphers,
  112. const char *prefciphers)
  113. {
  114. #if !defined(OPENSSL_NO_ECDH) && defined(NID_X9_62_prime256v1)
  115. EC_KEY *ecdh;
  116. #endif
  117. char *ssl_name = nullptr;
  118. int ssl_op = 0, ssl_include = 0, ssl_exclude = 0;
  119. SSL_CTX_set_options(soap->ctx, SSL_OP_ALL);
  120. #if !defined(OPENSSL_NO_ECDH) && defined(NID_X9_62_prime256v1)
  121. ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
  122. if (ecdh != nullptr) {
  123. SSL_CTX_set_options(soap->ctx, SSL_OP_SINGLE_ECDH_USE);
  124. SSL_CTX_set_tmp_ecdh(soap->ctx, ecdh);
  125. EC_KEY_free(ecdh);
  126. }
  127. #endif
  128. ssl_name = strtok(protos, " ");
  129. while (ssl_name != nullptr) {
  130. int ssl_proto = 0;
  131. bool ssl_neg = false;
  132. if (*ssl_name == '!') {
  133. ++ssl_name;
  134. ssl_neg = true;
  135. }
  136. if (strcasecmp(ssl_name, SSL_TXT_SSLV3) == 0)
  137. ssl_proto = 0x02;
  138. #ifdef SSL_TXT_SSLV2
  139. else if (strcasecmp(ssl_name, SSL_TXT_SSLV2) == 0)
  140. ssl_proto = 0x01;
  141. #endif
  142. else if (strcasecmp(ssl_name, SSL_TXT_TLSV1) == 0)
  143. ssl_proto = 0x04;
  144. #ifdef SSL_TXT_TLSV1_1
  145. else if (strcasecmp(ssl_name, SSL_TXT_TLSV1_1) == 0)
  146. ssl_proto = 0x08;
  147. #endif
  148. #ifdef SSL_TXT_TLSV1_2
  149. else if (strcasecmp(ssl_name, SSL_TXT_TLSV1_2) == 0)
  150. ssl_proto = 0x10;
  151. #endif
  152. else {
  153. ec_log_crit("Unknown protocol \"%s\" in protos setting", ssl_name);
  154. return KCERR_CALL_FAILED;
  155. }
  156. if (ssl_neg)
  157. ssl_exclude |= ssl_proto;
  158. else
  159. ssl_include |= ssl_proto;
  160. ssl_name = strtok(nullptr, " ");
  161. }
  162. if (ssl_include != 0)
  163. // Exclude everything, except those that are included (and let excludes still override those)
  164. ssl_exclude |= 0x1f & ~ssl_include;
  165. if ((ssl_exclude & 0x01) != 0)
  166. ssl_op |= SSL_OP_NO_SSLv2;
  167. if ((ssl_exclude & 0x02) != 0)
  168. ssl_op |= SSL_OP_NO_SSLv3;
  169. if ((ssl_exclude & 0x04) != 0)
  170. ssl_op |= SSL_OP_NO_TLSv1;
  171. #ifdef SSL_OP_NO_TLSv1_1
  172. if ((ssl_exclude & 0x08) != 0)
  173. ssl_op |= SSL_OP_NO_TLSv1_1;
  174. #endif
  175. #ifdef SSL_OP_NO_TLSv1_2
  176. if ((ssl_exclude & 0x10) != 0)
  177. ssl_op |= SSL_OP_NO_TLSv1_2;
  178. #endif
  179. if (protos != nullptr)
  180. SSL_CTX_set_options(soap->ctx, ssl_op);
  181. if (ciphers && SSL_CTX_set_cipher_list(soap->ctx, ciphers) != 1) {
  182. ec_log_crit("Can not set SSL cipher list to \"%s\": %s",
  183. ciphers, ERR_error_string(ERR_get_error(), 0));
  184. return KCERR_CALL_FAILED;
  185. }
  186. if (parseBool(prefciphers))
  187. SSL_CTX_set_options(soap->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
  188. /* request certificate from client; it is OK if not present. */
  189. SSL_CTX_set_verify(soap->ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, nullptr);
  190. return erSuccess;
  191. }
  192. ECSoapServerConnection::ECSoapServerConnection(ECConfig *lpConfig) :
  193. m_lpConfig(lpConfig)
  194. {
  195. #ifdef USE_EPOLL
  196. m_lpDispatcher = new ECDispatcherEPoll(lpConfig, ECSoapServerConnection::CreatePipeSocketCallback, this);
  197. ec_log_info("Using epoll events");
  198. #else
  199. m_lpDispatcher = new ECDispatcherSelect(lpConfig, ECSoapServerConnection::CreatePipeSocketCallback, this);
  200. ec_log_info("Using select events");
  201. #endif
  202. }
  203. ECSoapServerConnection::~ECSoapServerConnection(void)
  204. {
  205. delete m_lpDispatcher;
  206. }
  207. ECRESULT ECSoapServerConnection::ListenTCP(const char* lpServerName, int nServerPort, bool bEnableGET)
  208. {
  209. ECRESULT er = erSuccess;
  210. int socket = SOAP_INVALID_SOCKET;
  211. struct soap *lpsSoap = NULL;
  212. if(lpServerName == NULL) {
  213. er = KCERR_INVALID_PARAMETER;
  214. goto exit;
  215. }
  216. //init soap
  217. lpsSoap = soap_new2(SOAP_IO_KEEPALIVE | SOAP_XML_TREE | SOAP_C_UTFSTRING, SOAP_IO_KEEPALIVE | SOAP_XML_TREE | SOAP_C_UTFSTRING);
  218. kopano_new_soap_listener(CONNECTION_TYPE_TCP, lpsSoap);
  219. if (bEnableGET)
  220. lpsSoap->fget = http_get;
  221. lpsSoap->sndbuf = lpsSoap->rcvbuf = 0;
  222. lpsSoap->bind_flags = SO_REUSEADDR;
  223. lpsSoap->socket = socket = soap_bind(lpsSoap, *lpServerName == '\0' ? NULL : lpServerName, nServerPort, 100);
  224. if (socket == -1) {
  225. ec_log_crit("Unable to bind to port %d: %s. This is usually caused by another process (most likely another server) already using this port. This program will terminate now.", nServerPort, lpsSoap->fault->faultstring);
  226. kill(0, SIGTERM);
  227. exit(1);
  228. }
  229. m_lpDispatcher->AddListenSocket(lpsSoap);
  230. // Manually check for attachments, independant of streaming support
  231. soap_post_check_mime_attachments(lpsSoap);
  232. ec_log_notice("Listening for TCP connections on port %d", nServerPort);
  233. exit:
  234. if (er != erSuccess && lpsSoap)
  235. soap_free(lpsSoap);
  236. return er;
  237. }
  238. ECRESULT ECSoapServerConnection::ListenSSL(const char* lpServerName, int nServerPort, bool bEnableGET, const char* lpszKeyFile, const char* lpszKeyPass, const char* lpszCAFile, const char* lpszCAPath)
  239. {
  240. ECRESULT er = erSuccess;
  241. int socket = SOAP_INVALID_SOCKET;
  242. struct soap *lpsSoap = NULL;
  243. char *server_ssl_protocols = strdup(m_lpConfig->GetSetting("server_ssl_protocols"));
  244. const char *server_ssl_ciphers = m_lpConfig->GetSetting("server_ssl_ciphers");
  245. auto pref_ciphers = m_lpConfig->GetSetting("server_ssl_prefer_server_ciphers");
  246. if(lpServerName == NULL) {
  247. er = KCERR_INVALID_PARAMETER;
  248. goto exit;
  249. }
  250. lpsSoap = soap_new2(SOAP_IO_KEEPALIVE | SOAP_XML_TREE | SOAP_C_UTFSTRING, SOAP_IO_KEEPALIVE | SOAP_XML_TREE | SOAP_C_UTFSTRING);
  251. kopano_new_soap_listener(CONNECTION_TYPE_SSL, lpsSoap);
  252. if (bEnableGET)
  253. lpsSoap->fget = http_get;
  254. lpsSoap->sndbuf = lpsSoap->rcvbuf = 0;
  255. if (soap_ssl_server_context(
  256. lpsSoap,
  257. SOAP_SSL_DEFAULT, // we set SSL_VERIFY_PEER and more soon ourselves
  258. lpszKeyFile, // key file
  259. lpszKeyPass, // key password
  260. lpszCAFile, // CA certificate file which signed clients
  261. lpszCAPath, // CA certificate path of thrusted sources
  262. NULL, // dh file, null == rsa
  263. NULL, // create random data on the fly (/dev/urandom is slow .. create file?)
  264. "EC") // unique name for SSL session cache
  265. )
  266. {
  267. soap_set_fault(lpsSoap);
  268. ec_log_crit("K-2170: Unable to setup ssl context: %s", *soap_faultdetail(lpsSoap));
  269. er = KCERR_CALL_FAILED;
  270. goto exit;
  271. }
  272. er = kc_ssl_options(lpsSoap, server_ssl_protocols, server_ssl_ciphers, pref_ciphers);
  273. if (er != erSuccess)
  274. goto exit;
  275. lpsSoap->bind_flags = SO_REUSEADDR;
  276. lpsSoap->socket = socket = soap_bind(lpsSoap, *lpServerName == '\0' ? NULL : lpServerName, nServerPort, 100);
  277. if (socket == -1) {
  278. ec_log_crit("Unable to bind to port %d: %s (SSL). This is usually caused by another process (most likely another server) already using this port. This program will terminate now.", nServerPort, lpsSoap->fault->faultstring);
  279. kill(0, SIGTERM);
  280. exit(1);
  281. }
  282. m_lpDispatcher->AddListenSocket(lpsSoap);
  283. // Manually check for attachments, independant of streaming support
  284. soap_post_check_mime_attachments(lpsSoap);
  285. ec_log_notice("Listening for SSL connections on port %d", nServerPort);
  286. exit:
  287. free(server_ssl_protocols);
  288. if (er != erSuccess && lpsSoap != nullptr)
  289. soap_free(lpsSoap);
  290. return er;
  291. }
  292. ECRESULT ECSoapServerConnection::ListenPipe(const char* lpPipeName, bool bPriority)
  293. {
  294. ECRESULT er = erSuccess;
  295. int sPipe = -1;
  296. struct soap *lpsSoap = NULL;
  297. if(lpPipeName == NULL) {
  298. er = KCERR_INVALID_PARAMETER;
  299. goto exit;
  300. }
  301. //init soap
  302. lpsSoap = soap_new2(SOAP_IO_KEEPALIVE | SOAP_XML_TREE | SOAP_C_UTFSTRING, SOAP_IO_KEEPALIVE | SOAP_XML_TREE | SOAP_C_UTFSTRING);
  303. if (bPriority)
  304. kopano_new_soap_listener(CONNECTION_TYPE_NAMED_PIPE_PRIORITY, lpsSoap);
  305. else
  306. kopano_new_soap_listener(CONNECTION_TYPE_NAMED_PIPE, lpsSoap);
  307. // Create a Unix or Windows pipe
  308. m_strPipeName = lpPipeName;
  309. lpsSoap->sndbuf = lpsSoap->rcvbuf = 0;
  310. // set the mode stricter for the priority socket: let only the same Unix user or root connect on the priority socket, users should not be able to abuse the socket
  311. lpsSoap->socket = sPipe = create_pipe_socket(m_strPipeName.c_str(), m_lpConfig, true, bPriority ? 0660 : 0666);
  312. // This just marks the socket as being a pipe, which triggers some slightly different behaviour
  313. strcpy(lpsSoap->path,"pipe");
  314. if (sPipe == -1) {
  315. er = KCERR_CALL_FAILED;
  316. goto exit;
  317. }
  318. lpsSoap->master = sPipe;
  319. lpsSoap->peerlen = 0;
  320. memset(&lpsSoap->peer, 0, sizeof(lpsSoap->peer));
  321. m_lpDispatcher->AddListenSocket(lpsSoap);
  322. // Manually check for attachments, independant of streaming support
  323. soap_post_check_mime_attachments(lpsSoap);
  324. ec_log_notice("Listening for %spipe connections on %s", bPriority ? "priority " : "", lpPipeName);
  325. exit:
  326. if (er != erSuccess && lpsSoap != nullptr)
  327. soap_free(lpsSoap);
  328. return er;
  329. }
  330. SOAP_SOCKET ECSoapServerConnection::CreatePipeSocketCallback(void *lpParam)
  331. {
  332. auto lpThis = static_cast<ECSoapServerConnection *>(lpParam);
  333. return create_pipe_socket(lpThis->m_strPipeName.c_str(), lpThis->m_lpConfig, false, 0666);
  334. }
  335. ECRESULT ECSoapServerConnection::ShutDown()
  336. {
  337. return m_lpDispatcher->ShutDown();
  338. }
  339. ECRESULT ECSoapServerConnection::DoHUP()
  340. {
  341. return m_lpDispatcher->DoHUP();
  342. }
  343. ECRESULT ECSoapServerConnection::MainLoop()
  344. {
  345. return m_lpDispatcher->MainLoop();
  346. }
  347. ECRESULT ECSoapServerConnection::NotifyDone(struct soap *soap)
  348. {
  349. return m_lpDispatcher->NotifyDone(soap);
  350. }
  351. ECRESULT ECSoapServerConnection::GetStats(unsigned int *lpulQueueLength, double *lpdblAge,unsigned int *lpulThreadCount, unsigned int *lpulIdleThreads)
  352. {
  353. ECRESULT er = m_lpDispatcher->GetQueueLength(lpulQueueLength);
  354. if(er != erSuccess)
  355. return er;
  356. er = m_lpDispatcher->GetFrontItemAge(lpdblAge);
  357. if(er != erSuccess)
  358. return er;
  359. er = m_lpDispatcher->GetThreadCount(lpulThreadCount, lpulIdleThreads);
  360. if(er != erSuccess)
  361. return er;
  362. return erSuccess;
  363. }