auth-worker-client.c revision 5afc76d0215c5f7631dec06ef864d59f0686a0a8
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* Copyright (c) 2005-2013 Dovecot authors, see the included COPYING file */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#define CLIENT_STATE_STOP "waiting for shutdown"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic void auth_worker_input(struct auth_worker_client *client);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic int auth_worker_output(struct auth_worker_client *client);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenvoid auth_worker_refresh_proctitle(const char *state)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (!global_auth_settings->verbose_proctitle || !worker)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen process_title_set(t_strdup_printf("worker: %s", state));
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenauth_worker_client_check_throttle(struct auth_worker_client *client)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (o_stream_get_buffer_used_size(client->output) >=
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* stop reading new requests until client has read the pending
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenworker_auth_request_new(struct auth_worker_client *client, unsigned int id,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *const *args)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen (void)auth_request_import(auth_request, *args, NULL);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen (void)auth_request_import(auth_request, key, value);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic void auth_worker_send_reply(struct auth_worker_client *client,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen o_stream_nsend_str(client->output, "RESTART\n");
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen o_stream_nsend(client->output, str_data(str), str_len(str));
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenreply_append_extra_fields(string_t *str, struct auth_request *request)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (!auth_fields_is_empty(request->extra_fields)) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen auth_fields_append(request->extra_fields, str, 0, 0);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen auth_fields_is_empty(request->userdb_reply)) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* all userdb_* fields had NULL values. we'll still
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen need to tell this to the master */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen str_append(str, "\tuserdb_"AUTH_REQUEST_USER_KEY_IGNORE);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic void verify_plain_callback(enum passdb_result result,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct auth_worker_client *client = request->context;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (request->failed && result == PASSDB_RESULT_OK)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (result != PASSDB_RESULT_INTERNAL_FAILURE) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen str_append_tabescaped(str, request->passdb_password);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenauth_worker_handle_passv(struct auth_worker_client *client,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* verify plaintext password */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* <passdb id> <password> [<args>] */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (str_to_uint(args[0], &passdb_id) < 0 || args[1] == NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("BUG: Auth worker server sent us invalid PASSV");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen auth_request = worker_auth_request_new(client, id, args + 2);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen while (passdb != NULL && passdb->passdb->id != passdb_id)
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen /* could be a masterdb */
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen passdb = auth_request_get_auth(auth_request)->masterdbs;
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen while (passdb != NULL && passdb->passdb->id != passdb_id)
d3eff05aaa4c2bc0a7580ee87a54f6693f4a8241Timo Sirainen verify_plain(auth_request, password, verify_plain_callback);
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainenlookup_credentials_callback(enum passdb_result result,
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen const unsigned char *credentials, size_t size,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct auth_worker_client *client = request->context;
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen if (request->failed && result == PASSDB_RESULT_OK)
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen str_printfa(str, "\t{%s.b64}", request->credentials_scheme);
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainenauth_worker_handle_passl(struct auth_worker_client *client,
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen /* lookup credentials */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* <passdb id> <scheme> [<args>] */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (str_to_uint(args[0], &passdb_id) < 0 || args[1] == NULL) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_error("BUG: Auth worker server sent us invalid PASSL");
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen auth_request = worker_auth_request_new(client, id, args + 2);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen auth_request->credentials_scheme = p_strdup(auth_request->pool, scheme);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen while (auth_request->passdb->passdb->id != passdb_id) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen auth_request->passdb = auth_request->passdb->next;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (auth_request->passdb->passdb->iface.lookup_credentials == NULL) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_error("BUG: PASSL lookup not supported by given passdb");
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen auth_request->prefer_plain_credentials = TRUE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen lookup_credentials(auth_request, lookup_credentials_callback);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenset_credentials_callback(bool success, struct auth_request *request)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct auth_worker_client *client = request->context;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str_printfa(str, "%u\t%s\n", request->id, success ? "OK" : "FAIL");
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenauth_worker_handle_setcred(struct auth_worker_client *client,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* <passdb id> <credentials> [<args>] */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (str_to_uint(args[0], &passdb_id) < 0 || args[1] == NULL) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_error("BUG: Auth worker server sent us invalid SETCRED");
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen auth_request = worker_auth_request_new(client, id, args + 2);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_error("BUG: SETCRED had missing parameters");
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen while (auth_request->passdb->passdb->id != passdb_id) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen auth_request->passdb = auth_request->passdb->next;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_error("BUG: SETCRED had invalid passdb ID");
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen set_credentials(auth_request, creds, set_credentials_callback);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenlookup_user_callback(enum userdb_result result,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct auth_worker_client *client = auth_request->context;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen auth_fields_append(auth_request->userdb_reply, str, 0, 0);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstatic struct auth_userdb *
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenauth_userdb_find_by_id(struct auth_userdb *userdbs, unsigned int id)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen for (db = userdbs; db != NULL; db = db->next) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenauth_worker_handle_user(struct auth_worker_client *client,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* lookup user */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* <userdb id> [<args>] */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_error("BUG: Auth worker server sent us invalid USER");
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen auth_request = worker_auth_request_new(client, id, args + 1);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen auth_userdb_find_by_id(auth_request->userdb, userdb_id);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenauth_worker_client_idle_kill(struct auth_worker_client *client ATTR_UNUSED)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainenauth_worker_client_set_idle_timeout(struct auth_worker_client *client)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen idle_kill_secs = master_service_get_idle_kill_secs(master_service);
b92813e2f96d4b28f989528ed5dd6115da7d9bdbTimo Sirainen client->to_idle = timeout_add(idle_kill_secs * 1000,
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainenstatic void list_iter_deinit(struct auth_worker_list_context *ctx)
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen struct auth_worker_client *client = ctx->client;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (ctx->auth_request->userdb->userdb->iface->
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen str_printfa(str, "%u\tFAIL\n", ctx->auth_request->id);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen str_printfa(str, "%u\tOK\n", ctx->auth_request->id);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen client->io = io_add(client->fd, IO_READ, auth_worker_input, client);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen o_stream_set_flush_callback(client->output, auth_worker_output, client);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_IDLE);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainenstatic void list_iter_callback(const char *user, void *context)
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen struct auth_worker_list_context *ctx = context;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_printfa(str, "%u\t*\t%s\n", ctx->auth_request->id, user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen o_stream_nsend(ctx->client->output, str_data(str), str_len(str));
b92813e2f96d4b28f989528ed5dd6115da7d9bdbTimo Sirainen /* avoid recursively looping to this same function */
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen if (o_stream_get_buffer_used_size(ctx->client->output) > OUTBUF_THROTTLE_SIZE) {
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen if (o_stream_flush(ctx->client->output) < 0) {
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen o_stream_get_buffer_used_size(ctx->client->output) <= OUTBUF_THROTTLE_SIZE);
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen o_stream_set_flush_pending(ctx->client->output, TRUE);
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainenstatic int auth_worker_list_output(struct auth_worker_list_context *ctx)
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen if ((ret = o_stream_flush(ctx->client->output)) < 0) {
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainenauth_worker_handle_list(struct auth_worker_client *client,
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen i_error("BUG: Auth worker server sent us invalid LIST");
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen userdb = auth_userdb_find_by_id(client->auth->userdbs, userdb_id);
659fe5d24825b160cae512538088020d97a60239Timo Sirainen ctx = i_new(struct auth_worker_list_context, 1);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen ctx->auth_request = worker_auth_request_new(client, id, args + 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen o_stream_set_flush_callback(ctx->client->output,
6f66e585998aa88a4b0ccad531d329a103325d57Timo Sirainen ctx->iter = ctx->auth_request->userdb->userdb->iface->
6f66e585998aa88a4b0ccad531d329a103325d57Timo Sirainen iterate_init(ctx->auth_request, list_iter_callback, ctx);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen ctx->auth_request->userdb->userdb->iface->iterate_next(ctx->iter);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainenauth_worker_handle_line(struct auth_worker_client *client, const char *line)
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen const char *const *args;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen unsigned int id;
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen if (args[0] == NULL || args[1] == NULL || args[2] == NULL ||
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen ret = auth_worker_handle_passv(client, id, args + 2);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen ret = auth_worker_handle_passl(client, id, args + 2);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen ret = auth_worker_handle_setcred(client, id, args + 2);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen ret = auth_worker_handle_user(client, id, args + 2);
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen ret = auth_worker_handle_list(client, id, args + 2);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen i_error("BUG: Auth-worker received unknown command: %s",
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_IDLE);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainenstatic bool auth_worker_verify_db_hash(const char *line)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen binary_to_hex_append(str, passdb_md5, sizeof(passdb_md5));
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen binary_to_hex_append(str, userdb_md5, sizeof(userdb_md5));
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainenstatic void auth_worker_input(struct auth_worker_client *client)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* disconnected */
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen /* buffer full */
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen i_error("BUG: Auth worker server sent us more than %d bytes",
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (!version_string_verify(line, "auth-worker",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("Auth worker not compatible with this server "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "(mixed old and new binaries?)");
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen i_error("Auth worker sees different passdbs/userdbs "
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen "than auth server. Maybe config just changed "
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen "and this goes away automatically?");
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_IDLE);
6f66e585998aa88a4b0ccad531d329a103325d57Timo Sirainen while ((line = i_stream_next_line(client->input)) != NULL) {
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainenstatic int auth_worker_output(struct auth_worker_client *client)
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen if (o_stream_get_buffer_used_size(client->output) <=
0b3662995e9fa0d0d857ec5350ce2b1ee6d3b94fTimo Sirainen OUTBUF_THROTTLE_SIZE/3 && client->io == NULL) {
0b3662995e9fa0d0d857ec5350ce2b1ee6d3b94fTimo Sirainen /* allow input again */
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainenauth_worker_client_create(struct auth *auth, int fd)
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen client->input = i_stream_create_fd(fd, AUTH_WORKER_MAX_LINE_LENGTH,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen o_stream_set_no_error_handling(client->output, TRUE);
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen o_stream_set_flush_callback(client->output, auth_worker_output, client);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client->io = io_add(fd, IO_READ, auth_worker_input, client);
a87e5f15283e057c7dc26dd9db7b616268c95ca7Timo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_HANDSHAKE);
a87e5f15283e057c7dc26dd9db7b616268c95ca7Timo Sirainenvoid auth_worker_client_destroy(struct auth_worker_client **_client)
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen master_service_client_connection_destroyed(master_service);
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainenvoid auth_worker_client_unref(struct auth_worker_client **_client)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen o_stream_nsend_str(auth_worker_client->output, "ERROR\n");
d3eff05aaa4c2bc0a7580ee87a54f6693f4a8241Timo Sirainen o_stream_nsend_str(auth_worker_client->output, "SUCCESS\n");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_IDLE);
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen o_stream_nsend_str(auth_worker_client->output, "SHUTDOWN\n");