auth-server-connection.c revision 991367db5ec720fa0b764bbd6df21b7f7c654d2c
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include "lib.h"
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include "array.h"
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include "hash.h"
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include "ioloop.h"
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include "istream.h"
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include "ostream.h"
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include "network.h"
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include "eacces-error.h"
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include "auth-client-private.h"
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include "auth-client-request.h"
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include "auth-server-connection.h"
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include <unistd.h>
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#include <stdlib.h>
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#define AUTH_SERVER_CONN_MAX_LINE_LENGTH AUTH_CLIENT_MAX_LINE_LENGTH
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#define AUTH_HANDSHAKE_TIMEOUT (30*1000)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek#define AUTH_SERVER_RECONNECT_TIMEOUT_SECS 5
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekstatic void
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekauth_server_connection_disconnect(struct auth_server_connection *conn);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekstatic int
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekauth_server_input_mech(struct auth_server_connection *conn,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek const char *const *args)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek{
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek struct auth_mech_desc mech_desc;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (conn->handshake_received) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek i_error("BUG: Authentication server already sent handshake");
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek }
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (args[0] == NULL) {
d43c9d18fb263b1ea4071b20e93ce4994583f62fJakub Hrozek i_error("BUG: Authentication server sent broken MECH line");
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek }
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek memset(&mech_desc, 0, sizeof(mech_desc));
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek mech_desc.name = p_strdup(conn->pool, args[0]);
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek if (strcmp(mech_desc.name, "PLAIN") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek conn->has_plain_mech = TRUE;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek for (args++; *args != NULL; args++) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (strcmp(*args, "private") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek mech_desc.flags |= MECH_SEC_PRIVATE;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek else if (strcmp(*args, "anonymous") == 0)
12e7e87ccbae0d5c2f338cd019ca51556cbcd3aeLukas Slebodnik mech_desc.flags |= MECH_SEC_ANONYMOUS;
12e7e87ccbae0d5c2f338cd019ca51556cbcd3aeLukas Slebodnik else if (strcmp(*args, "plaintext") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek mech_desc.flags |= MECH_SEC_PLAINTEXT;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek else if (strcmp(*args, "dictionary") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek mech_desc.flags |= MECH_SEC_DICTIONARY;
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek else if (strcmp(*args, "active") == 0)
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek mech_desc.flags |= MECH_SEC_ACTIVE;
5a4239490c7fb7d732180a9d40f27f0247c56631Jakub Hrozek else if (strcmp(*args, "forward-secrecy") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek mech_desc.flags |= MECH_SEC_FORWARD_SECRECY;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek else if (strcmp(*args, "mutual-auth") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek mech_desc.flags |= MECH_SEC_MUTUAL_AUTH;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek }
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek array_append(&conn->available_auth_mechs, &mech_desc, 1);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return 0;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek}
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekstatic int
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekauth_server_input_spid(struct auth_server_connection *conn,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek const char *const *args)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek{
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (conn->handshake_received) {
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov i_error("BUG: Authentication server already sent handshake");
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek }
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov conn->server_pid = (unsigned int)strtoul(args[0], NULL, 10);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return 0;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek}
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashovstatic int
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekauth_server_input_cuid(struct auth_server_connection *conn,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek const char *const *args)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek{
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (conn->handshake_received) {
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov i_error("BUG: Authentication server already sent handshake");
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek }
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (args[0] == NULL) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek i_error("BUG: Authentication server sent broken CUID line");
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek }
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek conn->connect_uid = (unsigned int)strtoul(args[0], NULL, 10);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return 0;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek}
04868f1573f4b26ef34610b6d7069172f93bd8abJakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekstatic int
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekauth_server_input_cookie(struct auth_server_connection *conn,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek const char *const *args)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek{
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl if (conn->cookie != NULL) {
4cae8609b513c267af11c0409bfe1d17d3a5da2fMichal Zidek i_error("BUG: Authentication server already sent cookie");
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek }
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek conn->cookie = p_strdup(conn->pool, args[0]);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return 0;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek}
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekstatic int auth_server_input_done(struct auth_server_connection *conn)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek{
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl if (array_count(&conn->available_auth_mechs) == 0) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek i_error("BUG: Authentication server returned no mechanisms");
12e7e87ccbae0d5c2f338cd019ca51556cbcd3aeLukas Slebodnik return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek }
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (conn->cookie == NULL) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek i_error("BUG: Authentication server didn't send a cookie");
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek }
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (conn->to != NULL)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek timeout_remove(&conn->to);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek conn->handshake_received = TRUE;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (conn->client->connect_notify_callback != NULL) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek conn->client->connect_notify_callback(conn->client, TRUE,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek conn->client->connect_notify_context);
828cc04cd1ed9076faa6e1545055ae69a04f0f0fLukas Slebodnik }
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return 0;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek}
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekstatic int
4cae8609b513c267af11c0409bfe1d17d3a5da2fMichal Zidekauth_server_lookup_request(struct auth_server_connection *conn,
4cae8609b513c267af11c0409bfe1d17d3a5da2fMichal Zidek const char *id_arg, bool remove,
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl struct auth_client_request **request_r)
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl{
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl struct auth_client_request *request;
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl unsigned int id;
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl if (id_arg == NULL) {
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl i_error("BUG: Authentication server input missing ID");
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl return -1;
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl }
4cae8609b513c267af11c0409bfe1d17d3a5da2fMichal Zidek id = (unsigned int)strtoul(id_arg, NULL, 10);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek request = hash_table_lookup(conn->requests, POINTER_CAST(id));
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (request == NULL) {
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl i_error("BUG: Authentication server sent unknown id %u", id);
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl return -1;
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl }
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl if (remove || auth_client_request_is_aborted(request))
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl hash_table_remove(conn->requests, POINTER_CAST(id));
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl *request_r = request;
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl return 0;
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl}
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichlstatic int
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekauth_server_input_ok(struct auth_server_connection *conn,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek const char *const *args)
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl{
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek struct auth_client_request *request;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
4cae8609b513c267af11c0409bfe1d17d3a5da2fMichal Zidek if (auth_server_lookup_request(conn, args[0], TRUE, &request) < 0)
4cae8609b513c267af11c0409bfe1d17d3a5da2fMichal Zidek return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek auth_client_request_server_input(request, AUTH_REQUEST_STATUS_OK,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek args + 1);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return 0;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek}
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekstatic int auth_server_input_cont(struct auth_server_connection *conn,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek const char *const *args)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek{
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek struct auth_client_request *request;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (str_array_length(args) < 2) {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek i_error("BUG: Authentication server sent broken CONT line");
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek }
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (auth_server_lookup_request(conn, args[0], FALSE, &request) < 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek auth_client_request_server_input(request, AUTH_REQUEST_STATUS_CONTINUE,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek args + 1);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return 0;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek}
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekstatic int auth_server_input_fail(struct auth_server_connection *conn,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek const char *const *args)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek{
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek struct auth_client_request *request;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl if (auth_server_lookup_request(conn, args[0], TRUE, &request) < 0)
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek auth_client_request_server_input(request, AUTH_REQUEST_STATUS_FAIL,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek args + 1);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return 0;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek}
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichlstatic int
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichlauth_server_connection_input_line(struct auth_server_connection *conn,
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl const char *line)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek{
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek const char *const *args;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (conn->client->debug)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek i_debug("auth input: %s", line);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek args = t_strsplit(line, "\t");
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if (strcmp(args[0], "OK") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return auth_server_input_ok(conn, args + 1);
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl else if (strcmp(args[0], "CONT") == 0)
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl return auth_server_input_cont(conn, args + 1);
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl else if (strcmp(args[0], "FAIL") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return auth_server_input_fail(conn, args + 1);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek else if (strcmp(args[0], "MECH") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return auth_server_input_mech(conn, args + 1);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek else if (strcmp(args[0], "SPID") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return auth_server_input_spid(conn, args + 1);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek else if (strcmp(args[0], "CUID") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return auth_server_input_cuid(conn, args + 1);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek else if (strcmp(args[0], "COOKIE") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return auth_server_input_cookie(conn, args + 1);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek else if (strcmp(args[0], "DONE") == 0)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return auth_server_input_done(conn);
e210ed5da220acebb6751db4466fe352de08eaebLukas Slebodnik else {
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek i_error("Auth server sent unknown command: %s", args[0]);
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return -1;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek }
12e7e87ccbae0d5c2f338cd019ca51556cbcd3aeLukas Slebodnik}
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozekstatic void auth_server_connection_input(struct auth_server_connection *conn)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek{
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik const char *line;
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik int ret;
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik switch (i_stream_read(conn->input)) {
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik case 0:
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik return;
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik case -1:
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik /* disconnected */
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl auth_server_connection_disconnect(conn);
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl return;
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl case -2:
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik /* buffer full - can't happen unless auth is buggy */
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik i_error("BUG: Auth server sent us more than %d bytes of data",
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik AUTH_SERVER_CONN_MAX_LINE_LENGTH);
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik auth_server_connection_disconnect(conn);
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik return;
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik }
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik if (!conn->version_received) {
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik line = i_stream_next_line(conn->input);
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik if (line == NULL)
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik return;
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik /* make sure the major version matches */
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik if (strncmp(line, "VERSION\t", 8) != 0 ||
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik atoi(t_strcut(line + 8, '\t')) !=
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik AUTH_CLIENT_PROTOCOL_MAJOR_VERSION) {
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik i_error("Authentication server not compatible with "
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik "this client (mixed old and new binaries?)");
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik auth_server_connection_disconnect(conn);
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik return;
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik }
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik conn->version_received = TRUE;
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik }
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik while ((line = i_stream_next_line(conn->input)) != NULL) {
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik T_BEGIN {
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik ret = auth_server_connection_input_line(conn, line);
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik } T_END;
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik if (ret < 0) {
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik auth_server_connection_disconnect(conn);
1a783fb0be9a48a0abdfe8b52fce551d530487ceLukas Slebodnik break;
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichl }
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichl }
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichl}
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichl
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichlstruct auth_server_connection *
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichlauth_server_connection_init(struct auth_client *client)
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl{
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl struct auth_server_connection *conn;
1112e84494bcfd0f658e073d25f15ed877d047aaPavel Reichl pool_t pool;
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichl
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichl pool = pool_alloconly_create("auth server connection", 1024);
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichl conn = p_new(pool, struct auth_server_connection, 1);
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichl conn->pool = pool;
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichl
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichl conn->client = client;
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichl conn->fd = -1;
aa3fd6fde3888c0e333cad852ae5b4f671d55f58Pavel Reichl conn->requests = hash_table_create(default_pool, pool, 100, NULL, NULL);
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl i_array_init(&conn->available_auth_mechs, 8);
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl return conn;
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl}
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichlstatic void
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichlauth_server_connection_remove_requests(struct auth_server_connection *conn)
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl{
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl static const char *const temp_failure_args[] = { "temp", NULL };
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl struct hash_iterate_context *iter;
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl void *key, *value;
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl iter = hash_table_iterate_init(conn->requests);
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl while (hash_table_iterate(iter, &key, &value)) {
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl struct auth_client_request *request = value;
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl auth_client_request_server_input(request,
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl AUTH_REQUEST_STATUS_FAIL,
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl temp_failure_args);
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl }
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl hash_table_iterate_deinit(&iter);
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl}
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichlstatic void auth_server_connection_close(struct auth_server_connection *conn)
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl{
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl conn->handshake_received = FALSE;
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl conn->version_received = FALSE;
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl conn->has_plain_mech = FALSE;
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl conn->server_pid = 0;
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl conn->connect_uid = 0;
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl conn->cookie = NULL;
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl array_clear(&conn->available_auth_mechs);
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl if (conn->to != NULL)
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl timeout_remove(&conn->to);
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl if (conn->io != NULL)
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl io_remove(&conn->io);
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl if (conn->fd != -1) {
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl i_stream_destroy(&conn->input);
6fd5306145d98ea3bab7f32aa66475f610f388cePavel Reichl o_stream_destroy(&conn->output);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl if (close(conn->fd) < 0)
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl i_error("close(auth server connection) failed: %m");
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->fd = -1;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl }
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl auth_server_connection_remove_requests(conn);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl if (conn->client->connect_notify_callback != NULL) {
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->client->connect_notify_callback(conn->client, FALSE,
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->client->connect_notify_context);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl }
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl}
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichlstatic void auth_server_reconnect_timeout(struct auth_server_connection *conn)
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl{
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl timeout_remove(&conn->to);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl (void)auth_server_connection_connect(conn);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl}
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichlstatic void
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichlauth_server_connection_disconnect(struct auth_server_connection *conn)
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl{
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl time_t next_connect;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl auth_server_connection_close(conn);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl next_connect = conn->last_connect + AUTH_SERVER_RECONNECT_TIMEOUT_SECS;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->to = timeout_add(ioloop_time >= next_connect ? 0 :
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl (next_connect - ioloop_time) * 1000,
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl auth_server_reconnect_timeout, conn);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl}
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichlvoid auth_server_connection_deinit(struct auth_server_connection **_conn)
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl{
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl struct auth_server_connection *conn = *_conn;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl *_conn = NULL;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl auth_server_connection_close(conn);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl hash_table_destroy(&conn->requests);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl array_free(&conn->available_auth_mechs);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl pool_unref(&conn->pool);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl}
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichlstatic void auth_client_handshake_timeout(struct auth_server_connection *conn)
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl{
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl i_error("Timeout waiting for handshake from auth server. "
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl "my pid=%u, input bytes=%"PRIuUOFF_T,
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->client->client_pid, conn->input->v_offset);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl auth_server_connection_disconnect(conn);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl}
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichlint auth_server_connection_connect(struct auth_server_connection *conn)
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl{
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl const char *handshake;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl int fd;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl i_assert(conn->fd == -1);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->last_connect = ioloop_time;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl /* max. 1 second wait here. */
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl fd = net_connect_unix_with_retries(conn->client->auth_socket_path,
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl 1000);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl if (fd == -1) {
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl if (errno == EACCES) {
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl i_error("auth: %s",
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl eacces_error_get("connect",
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->client->auth_socket_path));
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl } else {
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl i_error("auth: connect(%s) failed: %m",
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->client->auth_socket_path);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl }
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl return -1;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl }
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->fd = fd;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->io = io_add(fd, IO_READ, auth_server_connection_input, conn);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->input = i_stream_create_fd(fd, AUTH_SERVER_CONN_MAX_LINE_LENGTH,
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl FALSE);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl handshake = t_strdup_printf("VERSION\t%u\t%u\nCPID\t%u\n",
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl AUTH_CLIENT_PROTOCOL_MAJOR_VERSION,
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl AUTH_CLIENT_PROTOCOL_MINOR_VERSION,
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->client->client_pid);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl if (o_stream_send_str(conn->output, handshake) < 0) {
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl i_warning("Error sending handshake to auth server: %m");
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl auth_server_connection_disconnect(conn);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl return -1;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl }
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl conn->to = timeout_add(AUTH_HANDSHAKE_TIMEOUT,
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl auth_client_handshake_timeout, conn);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl return 0;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl}
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichlunsigned int
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichlauth_server_connection_add_request(struct auth_server_connection *conn,
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl struct auth_client_request *request)
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl{
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl unsigned int id;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl id = ++conn->client->request_id_counter;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl if (id == 0) {
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl /* wrapped - ID 0 not allowed */
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl id = ++conn->client->request_id_counter;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl }
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl hash_table_insert(conn->requests, POINTER_CAST(id), request);
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl return id;
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl}
b0a8ed519554f8896e35812e0759862c33f157fePavel Reichl