auth-server-connection.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. /* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */
  2. #include "lib.h"
  3. #include "array.h"
  4. #include "hash.h"
  5. #include "hostpid.h"
  6. #include "ioloop.h"
  7. #include "istream.h"
  8. #include "ostream.h"
  9. #include "net.h"
  10. #include "strescape.h"
  11. #include "eacces-error.h"
  12. #include "auth-client-private.h"
  13. #include "auth-client-request.h"
  14. #include "auth-server-connection.h"
  15. #include <unistd.h>
  16. #define AUTH_SERVER_CONN_MAX_LINE_LENGTH AUTH_CLIENT_MAX_LINE_LENGTH
  17. #define AUTH_HANDSHAKE_TIMEOUT (30*1000)
  18. #define AUTH_SERVER_RECONNECT_TIMEOUT_SECS 5
  19. static void
  20. auth_server_connection_reconnect(struct auth_server_connection *conn,
  21. const char *disconnect_reason);
  22. static int
  23. auth_server_input_mech(struct auth_server_connection *conn,
  24. const char *const *args)
  25. {
  26. struct auth_mech_desc mech_desc;
  27. if (conn->handshake_received) {
  28. i_error("BUG: Authentication server already sent handshake");
  29. return -1;
  30. }
  31. if (args[0] == NULL) {
  32. i_error("BUG: Authentication server sent broken MECH line");
  33. return -1;
  34. }
  35. i_zero(&mech_desc);
  36. mech_desc.name = p_strdup(conn->pool, args[0]);
  37. if (strcmp(mech_desc.name, "PLAIN") == 0)
  38. conn->has_plain_mech = TRUE;
  39. for (args++; *args != NULL; args++) {
  40. if (strcmp(*args, "private") == 0)
  41. mech_desc.flags |= MECH_SEC_PRIVATE;
  42. else if (strcmp(*args, "anonymous") == 0)
  43. mech_desc.flags |= MECH_SEC_ANONYMOUS;
  44. else if (strcmp(*args, "plaintext") == 0)
  45. mech_desc.flags |= MECH_SEC_PLAINTEXT;
  46. else if (strcmp(*args, "dictionary") == 0)
  47. mech_desc.flags |= MECH_SEC_DICTIONARY;
  48. else if (strcmp(*args, "active") == 0)
  49. mech_desc.flags |= MECH_SEC_ACTIVE;
  50. else if (strcmp(*args, "forward-secrecy") == 0)
  51. mech_desc.flags |= MECH_SEC_FORWARD_SECRECY;
  52. else if (strcmp(*args, "mutual-auth") == 0)
  53. mech_desc.flags |= MECH_SEC_MUTUAL_AUTH;
  54. }
  55. array_append(&conn->available_auth_mechs, &mech_desc, 1);
  56. return 0;
  57. }
  58. static int
  59. auth_server_input_spid(struct auth_server_connection *conn,
  60. const char *const *args)
  61. {
  62. if (conn->handshake_received) {
  63. i_error("BUG: Authentication server already sent handshake");
  64. return -1;
  65. }
  66. if (str_to_uint(args[0], &conn->server_pid) < 0) {
  67. i_error("BUG: Authentication server sent invalid PID");
  68. return -1;
  69. }
  70. return 0;
  71. }
  72. static int
  73. auth_server_input_cuid(struct auth_server_connection *conn,
  74. const char *const *args)
  75. {
  76. if (conn->handshake_received) {
  77. i_error("BUG: Authentication server already sent handshake");
  78. return -1;
  79. }
  80. if (args[0] == NULL ||
  81. str_to_uint(args[0], &conn->connect_uid) < 0) {
  82. i_error("BUG: Authentication server sent broken CUID line");
  83. return -1;
  84. }
  85. return 0;
  86. }
  87. static int
  88. auth_server_input_cookie(struct auth_server_connection *conn,
  89. const char *const *args)
  90. {
  91. if (conn->cookie != NULL) {
  92. i_error("BUG: Authentication server already sent cookie");
  93. return -1;
  94. }
  95. conn->cookie = p_strdup(conn->pool, args[0]);
  96. return 0;
  97. }
  98. static int auth_server_input_done(struct auth_server_connection *conn)
  99. {
  100. if (array_count(&conn->available_auth_mechs) == 0) {
  101. i_error("BUG: Authentication server returned no mechanisms");
  102. return -1;
  103. }
  104. if (conn->cookie == NULL) {
  105. i_error("BUG: Authentication server didn't send a cookie");
  106. return -1;
  107. }
  108. if (conn->to != NULL)
  109. timeout_remove(&conn->to);
  110. conn->handshake_received = TRUE;
  111. if (conn->client->connect_notify_callback != NULL) {
  112. conn->client->connect_notify_callback(conn->client, TRUE,
  113. conn->client->connect_notify_context);
  114. }
  115. return 0;
  116. }
  117. static int
  118. auth_server_lookup_request(struct auth_server_connection *conn,
  119. const char *id_arg, bool remove,
  120. struct auth_client_request **request_r)
  121. {
  122. struct auth_client_request *request;
  123. unsigned int id;
  124. if (id_arg == NULL || str_to_uint(id_arg, &id) < 0) {
  125. i_error("BUG: Authentication server input missing ID");
  126. return -1;
  127. }
  128. request = hash_table_lookup(conn->requests, POINTER_CAST(id));
  129. if (request == NULL) {
  130. i_error("BUG: Authentication server sent unknown id %u", id);
  131. return -1;
  132. }
  133. if (remove || auth_client_request_is_aborted(request))
  134. hash_table_remove(conn->requests, POINTER_CAST(id));
  135. *request_r = request;
  136. return 0;
  137. }
  138. static int
  139. auth_server_input_ok(struct auth_server_connection *conn,
  140. const char *const *args)
  141. {
  142. struct auth_client_request *request;
  143. if (auth_server_lookup_request(conn, args[0], TRUE, &request) < 0)
  144. return -1;
  145. auth_client_request_server_input(request, AUTH_REQUEST_STATUS_OK,
  146. args + 1);
  147. return 0;
  148. }
  149. static int auth_server_input_cont(struct auth_server_connection *conn,
  150. const char *const *args)
  151. {
  152. struct auth_client_request *request;
  153. if (str_array_length(args) < 2) {
  154. i_error("BUG: Authentication server sent broken CONT line");
  155. return -1;
  156. }
  157. if (auth_server_lookup_request(conn, args[0], FALSE, &request) < 0)
  158. return -1;
  159. auth_client_request_server_input(request, AUTH_REQUEST_STATUS_CONTINUE,
  160. args + 1);
  161. return 0;
  162. }
  163. static int auth_server_input_fail(struct auth_server_connection *conn,
  164. const char *const *args)
  165. {
  166. struct auth_client_request *request;
  167. if (auth_server_lookup_request(conn, args[0], TRUE, &request) < 0)
  168. return -1;
  169. auth_client_request_server_input(request, AUTH_REQUEST_STATUS_FAIL,
  170. args + 1);
  171. return 0;
  172. }
  173. static int
  174. auth_server_connection_input_line(struct auth_server_connection *conn,
  175. const char *line)
  176. {
  177. const char *const *args;
  178. if (conn->client->debug)
  179. i_debug("auth input: %s", line);
  180. args = t_strsplit_tabescaped(line);
  181. if (args[0] == NULL) {
  182. i_error("Auth server sent empty line");
  183. return -1;
  184. }
  185. if (strcmp(args[0], "OK") == 0)
  186. return auth_server_input_ok(conn, args + 1);
  187. else if (strcmp(args[0], "CONT") == 0)
  188. return auth_server_input_cont(conn, args + 1);
  189. else if (strcmp(args[0], "FAIL") == 0)
  190. return auth_server_input_fail(conn, args + 1);
  191. else if (strcmp(args[0], "MECH") == 0)
  192. return auth_server_input_mech(conn, args + 1);
  193. else if (strcmp(args[0], "SPID") == 0)
  194. return auth_server_input_spid(conn, args + 1);
  195. else if (strcmp(args[0], "CUID") == 0)
  196. return auth_server_input_cuid(conn, args + 1);
  197. else if (strcmp(args[0], "COOKIE") == 0)
  198. return auth_server_input_cookie(conn, args + 1);
  199. else if (strcmp(args[0], "DONE") == 0)
  200. return auth_server_input_done(conn);
  201. else {
  202. i_error("Auth server sent unknown command: %s", args[0]);
  203. return -1;
  204. }
  205. }
  206. static void auth_server_connection_input(struct auth_server_connection *conn)
  207. {
  208. struct istream *input;
  209. const char *line, *error;
  210. int ret;
  211. switch (i_stream_read(conn->input)) {
  212. case 0:
  213. return;
  214. case -1:
  215. /* disconnected */
  216. error = conn->input->stream_errno != 0 ?
  217. strerror(conn->input->stream_errno) : "EOF";
  218. auth_server_connection_reconnect(conn, error);
  219. return;
  220. case -2:
  221. /* buffer full - can't happen unless auth is buggy */
  222. i_error("BUG: Auth server sent us more than %d bytes of data",
  223. AUTH_SERVER_CONN_MAX_LINE_LENGTH);
  224. auth_server_connection_disconnect(conn, "buffer full");
  225. return;
  226. }
  227. if (!conn->version_received) {
  228. line = i_stream_next_line(conn->input);
  229. if (line == NULL)
  230. return;
  231. /* make sure the major version matches */
  232. if (strncmp(line, "VERSION\t", 8) != 0 ||
  233. !str_uint_equals(t_strcut(line + 8, '\t'),
  234. AUTH_CLIENT_PROTOCOL_MAJOR_VERSION)) {
  235. i_error("Authentication server not compatible with "
  236. "this client (mixed old and new binaries?)");
  237. auth_server_connection_disconnect(conn,
  238. "incompatible server");
  239. return;
  240. }
  241. conn->version_received = TRUE;
  242. }
  243. input = conn->input;
  244. i_stream_ref(input);
  245. while ((line = i_stream_next_line(input)) != NULL && !input->closed) {
  246. T_BEGIN {
  247. ret = auth_server_connection_input_line(conn, line);
  248. } T_END;
  249. if (ret < 0) {
  250. auth_server_connection_disconnect(conn, t_strdup_printf(
  251. "Received broken input: %s", line));
  252. break;
  253. }
  254. }
  255. i_stream_unref(&input);
  256. }
  257. struct auth_server_connection *
  258. auth_server_connection_init(struct auth_client *client)
  259. {
  260. struct auth_server_connection *conn;
  261. pool_t pool;
  262. pool = pool_alloconly_create("auth server connection", 1024);
  263. conn = p_new(pool, struct auth_server_connection, 1);
  264. conn->pool = pool;
  265. conn->client = client;
  266. conn->fd = -1;
  267. hash_table_create_direct(&conn->requests, pool, 100);
  268. i_array_init(&conn->available_auth_mechs, 8);
  269. return conn;
  270. }
  271. static void
  272. auth_server_connection_remove_requests(struct auth_server_connection *conn,
  273. const char *disconnect_reason)
  274. {
  275. static const char *const temp_failure_args[] = { "temp", NULL };
  276. struct hash_iterate_context *iter;
  277. void *key;
  278. struct auth_client_request *request;
  279. time_t created, oldest = 0;
  280. unsigned int request_count = 0;
  281. if (hash_table_count(conn->requests) == 0)
  282. return;
  283. iter = hash_table_iterate_init(conn->requests);
  284. while (hash_table_iterate(iter, conn->requests, &key, &request)) {
  285. if (!auth_client_request_is_aborted(request)) {
  286. request_count++;
  287. created = auth_client_request_get_create_time(request);
  288. if (oldest > created || oldest == 0)
  289. oldest = created;
  290. }
  291. auth_client_request_server_input(request,
  292. AUTH_REQUEST_STATUS_INTERNAL_FAIL,
  293. temp_failure_args);
  294. }
  295. hash_table_iterate_deinit(&iter);
  296. hash_table_clear(conn->requests, FALSE);
  297. if (request_count > 0) {
  298. i_warning("Auth connection closed with %u pending requests "
  299. "(max %u secs, pid=%s, %s)", request_count,
  300. (unsigned int)(ioloop_time - oldest),
  301. my_pid, disconnect_reason);
  302. }
  303. }
  304. void auth_server_connection_disconnect(struct auth_server_connection *conn,
  305. const char *reason)
  306. {
  307. conn->handshake_received = FALSE;
  308. conn->version_received = FALSE;
  309. conn->has_plain_mech = FALSE;
  310. conn->server_pid = 0;
  311. conn->connect_uid = 0;
  312. conn->cookie = NULL;
  313. array_clear(&conn->available_auth_mechs);
  314. if (conn->to != NULL)
  315. timeout_remove(&conn->to);
  316. if (conn->io != NULL)
  317. io_remove(&conn->io);
  318. if (conn->fd != -1) {
  319. i_stream_destroy(&conn->input);
  320. o_stream_destroy(&conn->output);
  321. if (close(conn->fd) < 0)
  322. i_error("close(auth server connection) failed: %m");
  323. conn->fd = -1;
  324. }
  325. auth_server_connection_remove_requests(conn, reason);
  326. if (conn->client->connect_notify_callback != NULL) {
  327. conn->client->connect_notify_callback(conn->client, FALSE,
  328. conn->client->connect_notify_context);
  329. }
  330. }
  331. static void auth_server_reconnect_timeout(struct auth_server_connection *conn)
  332. {
  333. (void)auth_server_connection_connect(conn);
  334. }
  335. static void
  336. auth_server_connection_reconnect(struct auth_server_connection *conn,
  337. const char *disconnect_reason)
  338. {
  339. time_t next_connect;
  340. auth_server_connection_disconnect(conn, disconnect_reason);
  341. next_connect = conn->last_connect + AUTH_SERVER_RECONNECT_TIMEOUT_SECS;
  342. conn->to = timeout_add(ioloop_time >= next_connect ? 0 :
  343. (next_connect - ioloop_time) * 1000,
  344. auth_server_reconnect_timeout, conn);
  345. }
  346. void auth_server_connection_deinit(struct auth_server_connection **_conn)
  347. {
  348. struct auth_server_connection *conn = *_conn;
  349. *_conn = NULL;
  350. auth_server_connection_disconnect(conn, "deinitializing");
  351. i_assert(hash_table_count(conn->requests) == 0);
  352. hash_table_destroy(&conn->requests);
  353. array_free(&conn->available_auth_mechs);
  354. pool_unref(&conn->pool);
  355. }
  356. static void auth_client_handshake_timeout(struct auth_server_connection *conn)
  357. {
  358. i_error("Timeout waiting for handshake from auth server. "
  359. "my pid=%u, input bytes=%"PRIuUOFF_T,
  360. conn->client->client_pid, conn->input->v_offset);
  361. auth_server_connection_reconnect(conn, "auth server timeout");
  362. }
  363. int auth_server_connection_connect(struct auth_server_connection *conn)
  364. {
  365. const char *handshake;
  366. int fd;
  367. i_assert(conn->fd == -1);
  368. conn->last_connect = ioloop_time;
  369. if (conn->to != NULL)
  370. timeout_remove(&conn->to);
  371. /* max. 1 second wait here. */
  372. fd = net_connect_unix_with_retries(conn->client->auth_socket_path,
  373. 1000);
  374. if (fd == -1) {
  375. if (errno == EACCES) {
  376. i_error("auth: %s",
  377. eacces_error_get("connect",
  378. conn->client->auth_socket_path));
  379. } else {
  380. i_error("auth: connect(%s) failed: %m",
  381. conn->client->auth_socket_path);
  382. }
  383. return -1;
  384. }
  385. conn->fd = fd;
  386. conn->io = io_add(fd, IO_READ, auth_server_connection_input, conn);
  387. conn->input = i_stream_create_fd(fd, AUTH_SERVER_CONN_MAX_LINE_LENGTH,
  388. FALSE);
  389. conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
  390. handshake = t_strdup_printf("VERSION\t%u\t%u\nCPID\t%u\n",
  391. AUTH_CLIENT_PROTOCOL_MAJOR_VERSION,
  392. AUTH_CLIENT_PROTOCOL_MINOR_VERSION,
  393. conn->client->client_pid);
  394. if (o_stream_send_str(conn->output, handshake) < 0) {
  395. i_warning("Error sending handshake to auth server: %s",
  396. o_stream_get_error(conn->output));
  397. auth_server_connection_disconnect(conn,
  398. o_stream_get_error(conn->output));
  399. return -1;
  400. }
  401. conn->to = timeout_add(AUTH_HANDSHAKE_TIMEOUT,
  402. auth_client_handshake_timeout, conn);
  403. return 0;
  404. }
  405. unsigned int
  406. auth_server_connection_add_request(struct auth_server_connection *conn,
  407. struct auth_client_request *request)
  408. {
  409. unsigned int id;
  410. id = ++conn->client->request_id_counter;
  411. if (id == 0) {
  412. /* wrapped - ID 0 not allowed */
  413. id = ++conn->client->request_id_counter;
  414. }
  415. i_assert(hash_table_lookup(conn->requests, POINTER_CAST(id)) == NULL);
  416. hash_table_insert(conn->requests, POINTER_CAST(id), request);
  417. return id;
  418. }
  419. void auth_server_connection_remove_request(struct auth_server_connection *conn, unsigned int id)
  420. {
  421. i_assert(conn->handshake_received);
  422. hash_table_remove(conn->requests, POINTER_CAST(id));
  423. }