auth-worker-client.c revision b624773984e35dd894db8dff976c1a2114c70782
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2005-2011 Dovecot authors, see the included COPYING file */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic void auth_worker_input(struct auth_worker_client *client);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int auth_worker_output(struct auth_worker_client *client);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenauth_worker_client_check_throttle(struct auth_worker_client *client)
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen if (o_stream_get_buffer_used_size(client->output) >=
857c471c13ca215f4be9dd4b336b742b8d434e31Timo Sirainen /* stop reading new requests until client has read the pending
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenworker_auth_request_new(struct auth_worker_client *client, unsigned int id,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const char *const *args)
8b31f966d9688e07672ef1958dcbdb7686523c04Timo Sirainen (void)auth_request_import(auth_request, key, value);
d1e843e77f4760e303c53d9fce10123fc8d230a1Timo Sirainenstatic void auth_worker_send_reply(struct auth_worker_client *client,
291ce16fffca75e8598a8c9dceb08613413dcb07Timo Sirainen o_stream_send_str(client->output, "SHUTDOWN\n");
08e9fd42eb8007e1f9db62c088eef74f906114a5Josef 'Jeff' Sipek o_stream_send(client->output, str_data(str), str_len(str));
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainenstatic void verify_plain_callback(enum passdb_result result,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen struct auth_worker_client *client = request->context;
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen if (request->passdb_failure && result == PASSDB_RESULT_OK)
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen reply = auth_stream_reply_init(pool_datastack_create());
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen auth_stream_reply_add(reply, NULL, dec2str(request->id));
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (result != PASSDB_RESULT_INTERNAL_FAILURE) {
57a8c6a95e4bce3eeaba36985adb81c07dd683ffTimo Sirainen auth_stream_reply_add(reply, NULL, request->user);
afe1da042382720393eca6497253106e4eec75e0Timo Sirainen const char *fields =
afe1da042382720393eca6497253106e4eec75e0Timo Sirainen auth_stream_reply_export(request->extra_fields);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *fields =
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen auth_stream_reply_export(request->extra_cache_fields);
98c217499d578495e982ea6010ebff831e9669aeMartti Rannanjärviauth_worker_handle_passv(struct auth_worker_client *client,
98c217499d578495e982ea6010ebff831e9669aeMartti Rannanjärvi unsigned int id, const char *const *args)
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen /* verify plaintext password */
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen /* <passdb id> <password> [<args>] */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (str_to_uint(args[0], &passdb_id) < 0 || args[1] == NULL) {
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen i_error("BUG: Auth worker server sent us invalid PASSV");
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen auth_request = worker_auth_request_new(client, id, args + 2);
9fd2181788a61500641c66aec0f8c746b19bf830Timo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
98c217499d578495e982ea6010ebff831e9669aeMartti Rannanjärvi while (passdb != NULL && passdb->passdb->id != passdb_id)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* could be a masterdb */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen passdb = auth_request_get_auth(auth_request)->masterdbs;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen while (passdb != NULL && passdb->passdb->id != passdb_id)
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen verify_plain(auth_request, password, verify_plain_callback);
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainenlookup_credentials_callback(enum passdb_result result,
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen const unsigned char *credentials, size_t size,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen struct auth_worker_client *client = request->context;
cff1f182205e674285cf3ff446a0dcf7afea277dTimo Sirainen if (request->passdb_failure && result == PASSDB_RESULT_OK)
e03d986a74128f5ba30fcfda9f6e36578f5d8decTimo Sirainen reply = auth_stream_reply_init(pool_datastack_create());
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen auth_stream_reply_add(reply, NULL, dec2str(request->id));
d3280fe317a4598c0868cc440e7a1191c06d0db3Timo Sirainen auth_stream_reply_add(reply, NULL, request->user);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen str_printfa(str, "{%s.b64}", request->credentials_scheme);
6469cf211a57433335641725dc236ebb2b9fdd3bTimo Sirainen auth_stream_reply_add(reply, NULL, str_c(str));
6cc4cce2078aca138fbce19305e69e77edcee614Timo Sirainen const char *fields =
bd6a8056771b6150685dea319ab5a94e021d17f1Josef 'Jeff' Sipek auth_stream_reply_export(request->extra_fields);
bd6a8056771b6150685dea319ab5a94e021d17f1Josef 'Jeff' Sipek if (request->extra_cache_fields != NULL) {
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen const char *fields =
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen auth_stream_reply_export(request->extra_cache_fields);
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainenauth_worker_handle_passl(struct auth_worker_client *client,
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen /* lookup credentials */
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen /* <passdb id> <scheme> [<args>] */
2584e86cc2d8c31ba30a4109cf4ba09d1e37e28aTimo Sirainen if (str_to_uint(args[0], &passdb_id) < 0 || args[1] == NULL) {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen i_error("BUG: Auth worker server sent us invalid PASSL");
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen auth_request = worker_auth_request_new(client, id, args + 2);
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen auth_request->credentials_scheme = p_strdup(auth_request->pool, scheme);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen while (auth_request->passdb->passdb->id != passdb_id) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen auth_request->passdb = auth_request->passdb->next;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (auth_request->passdb->passdb->iface.lookup_credentials == NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("BUG: PASSL lookup not supported by given passdb");
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen auth_request->prefer_plain_credentials = TRUE;
f3bb2fbe87425dc89a839908985af496f7f65702Timo Sirainen lookup_credentials(auth_request, lookup_credentials_callback);
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainenset_credentials_callback(bool success, struct auth_request *request)
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen struct auth_worker_client *client = request->context;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_printfa(str, "%u\t%s\n", request->id, success ? "OK" : "FAIL");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenauth_worker_handle_setcred(struct auth_worker_client *client,
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen /* <passdb id> <credentials> [<args>] */
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen if (str_to_uint(args[0], &passdb_id) < 0 || args[1] == NULL) {
0177594fa5217b02001f4ec8752154fd2b05c545Timo Sirainen i_error("BUG: Auth worker server sent us invalid SETCRED");
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen auth_request = worker_auth_request_new(client, id, args + 2);
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen i_error("BUG: SETCRED had missing parameters");
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen while (auth_request->passdb->passdb->id != passdb_id) {
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen auth_request->passdb = auth_request->passdb->next;
1ae5d61ec366fdb2f3c5b150ca378d6141b0f4bdTimo Sirainen i_error("BUG: SETCRED had invalid passdb ID");
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen set_credentials(auth_request, creds, set_credentials_callback);
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainenlookup_user_callback(enum userdb_result result,
d938e9e4ec4c0f326dffd5ebe42c1ad893ce7e52Timo Sirainen struct auth_worker_client *client = auth_request->context;
eecb235c14b49c01774134ea593c266f2d2c2be1Timo Sirainen struct auth_stream_reply *reply = auth_request->userdb_reply;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen str_append(str, auth_stream_reply_export(reply));
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainenstatic struct auth_userdb *
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainenauth_userdb_find_by_id(struct auth_userdb *userdbs, unsigned int id)
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen for (db = userdbs; db != NULL; db = db->next) {
97a5e150cacf7da9dd200e317b75460352038d65Timo Sirainenauth_worker_handle_user(struct auth_worker_client *client,
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen /* lookup user */
817d027593510c3ba70ad542ce0011f5f6916d1eTimo Sirainen /* <userdb id> [<args>] */
6ae329de09afb7214c906d762320847e05469d53Timo Sirainen i_error("BUG: Auth worker server sent us invalid USER");
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen auth_request = worker_auth_request_new(client, id, args + 1);
7e2671b295927b461adc8b6c4ed6a1c4761fb323Timo Sirainen if (auth_request->user == NULL || auth_request->service == NULL) {
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen auth_userdb_find_by_id(auth_request->userdb, userdb_id);
3f91e60401495a4046c73992fabaa5e77200a451Timo Sirainenstatic void list_iter_deinit(struct auth_worker_list_context *ctx)
3857e2945a3b6744d603f0f5a656849ed8436ba3Timo Sirainen struct auth_worker_client *client = ctx->client;
58ba0fe5a6904d3a65cfe268411f4cbb881234eeTimo Sirainen if (ctx->auth_request->userdb->userdb->iface->
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen str_printfa(str, "%u\tFAIL\n", ctx->auth_request->id);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen str_printfa(str, "%u\tOK\n", ctx->auth_request->id);
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen client->io = io_add(client->fd, IO_READ, auth_worker_input, client);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen o_stream_set_flush_callback(client->output, auth_worker_output, client);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainenstatic void list_iter_callback(const char *user, void *context)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen struct auth_worker_list_context *ctx = context;
0d86aa0d47f7393c669c084b34c0537b193688adTimo Sirainen str_printfa(str, "%u\t*\t%s\n", ctx->auth_request->id, user);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen o_stream_send(ctx->client->output, str_data(str), str_len(str));
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen /* avoid recursively looping to this same function */
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainen o_stream_get_buffer_used_size(ctx->client->output) == 0);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstatic int auth_worker_list_output(struct auth_worker_list_context *ctx)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if ((ret = o_stream_flush(ctx->client->output)) < 0) {
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainenauth_worker_handle_list(struct auth_worker_client *client,
21aaa6affb9f134112b75b5db737309fc35ef1cfMartti Rannanjärvi if (str_to_uint(args[0], &userdb_id) < 0) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen i_error("BUG: Auth worker server sent us invalid LIST");
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen userdb = auth_userdb_find_by_id(client->auth->userdbs, userdb_id);
0206dc57f2c04da69599dea5816235cfeb2b897aMartti Rannanjärvi ctx = i_new(struct auth_worker_list_context, 1);
6564208826b0f46a00f010d1b5711d85944c3c88Timo Sirainen ctx->auth_request = worker_auth_request_new(client, id, args + 1);
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen o_stream_set_flush_callback(ctx->client->output,
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen ctx->iter = ctx->auth_request->userdb->userdb->iface->
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen iterate_init(ctx->auth_request, list_iter_callback, ctx);
13e130c3af3032982de6b1d13c6dcddda9164848Timo Sirainen ctx->auth_request->userdb->userdb->iface->iterate_next(ctx->iter);
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainenauth_worker_handle_line(struct auth_worker_client *client, const char *line)
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Sirainen const char *const *args;
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Sirainen unsigned int id;
2c8ca7e88ec881c473fb90e5f647c1f563877164Timo Sirainen if (args[0] == NULL || args[1] == NULL || args[2] == NULL ||
defb12ecd360df672ffb2f4dbf4d1218a0a9549cTimo Sirainen ret = auth_worker_handle_passv(client, id, args + 2);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = auth_worker_handle_passl(client, id, args + 2);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen ret = auth_worker_handle_setcred(client, id, args + 2);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen ret = auth_worker_handle_user(client, id, args + 2);
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen ret = auth_worker_handle_list(client, id, args + 2);
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen i_error("BUG: Auth-worker received unknown command: %s",
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_IDLE);
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainenstatic bool auth_worker_verify_db_hash(const char *line)
194603b35061fea1ee8d171a7104b6985c610966Timo Sirainen binary_to_hex_append(str, passdb_md5, sizeof(passdb_md5));
194603b35061fea1ee8d171a7104b6985c610966Timo Sirainen binary_to_hex_append(str, userdb_md5, sizeof(userdb_md5));
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainenstatic void auth_worker_input(struct auth_worker_client *client)
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen /* disconnected */
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen /* buffer full */
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen i_error("BUG: Auth worker server sent us more than %d bytes",
1eaaa2c9003cf3fbf672d597473e3f84e70d2ee6Timo Sirainen if (!version_string_verify(line, "auth-worker",
1eaaa2c9003cf3fbf672d597473e3f84e70d2ee6Timo Sirainen i_error("Auth worker not compatible with this server "
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen "(mixed old and new binaries?)");
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen i_error("Auth worker sees different passdbs/userdbs "
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen "than auth server. Maybe config just changed "
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen "and this goes away automatically?");
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_IDLE);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen while ((line = i_stream_next_line(client->input)) != NULL) {
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainenstatic int auth_worker_output(struct auth_worker_client *client)
61cae213dc69070c1cef266578057710e34e0d12Timo Sirainen if (o_stream_get_buffer_used_size(client->output) <=
0cce885512b836ce021260a58e7b4f099b36d0f1Timo Sirainen OUTBUF_THROTTLE_SIZE/3 && client->io == NULL) {
9617ac7078a17bd346fed69526861c3e7fd9d809Timo Sirainen /* allow input again */
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainenauth_worker_client_create(struct auth *auth, int fd)
b62140c5849297a800fee942026d9c3cb8c60206Timo Sirainen client->input = i_stream_create_fd(fd, AUTH_WORKER_MAX_LINE_LENGTH,
b62140c5849297a800fee942026d9c3cb8c60206Timo Sirainen client->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
b62140c5849297a800fee942026d9c3cb8c60206Timo Sirainen o_stream_set_flush_callback(client->output, auth_worker_output, client);
b09eaeb9a81e5b58c6e605eb762573a2b4a69e0eTimo Sirainen client->io = io_add(fd, IO_READ, auth_worker_input, client);
b09eaeb9a81e5b58c6e605eb762573a2b4a69e0eTimo Sirainen auth_worker_refresh_proctitle(CLIENT_STATE_HANDSHAKE);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainenvoid auth_worker_client_destroy(struct auth_worker_client **_client)
a4f09749814b93e8ad3ec8a0dc18885b874d6f8cTimo Sirainen master_service_client_connection_destroyed(master_service);