master-login.c revision 9ba5c6da815d5d4b43861387dd08fcea321a0423
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2009-2010 Dovecot authors, see the included COPYING file */
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen#define MASTER_LOGIN_POSTLOGIN_TIMEOUT_MSECS (60*1000)
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen master_login_failure_callback_t *failure_callback;
e871bfe83e8c5cc7768de30afe0127a3c4373adeTimo Sirainenstatic void master_login_conn_close(struct master_login_connection *conn);
4e56e6408815c04f2e5b904a648a366a2dcbd408Timo Sirainenstatic void master_login_conn_unref(struct master_login_connection **_conn);
3ba9a079592f46e94ce846e5aa80e4d479cd5e41Timo Sirainenmaster_login_init(struct master_service *service, const char *auth_socket_path,
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen master_login_failure_callback_t *failure_callback)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen login->auth = master_login_auth_init(auth_socket_path);
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen login->postlogin_socket_path = i_strdup(postlogin_socket_path);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenvoid master_login_deinit(struct master_login **_login)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct master_login_connection *conn = login->conns;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenmaster_login_conn_read_request(struct master_login_connection *conn,
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen unsigned char data[MASTER_AUTH_MAX_DATA_SIZE],
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen ret = fd_read(conn->fd, req_r, sizeof(*req_r), client_fd_r);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen /* disconnected */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen } else if (ret > 0) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen /* request wasn't fully read */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (req_r->data_size > MASTER_AUTH_MAX_DATA_SIZE) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen /* @UNSAFE */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen /* disconnected */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen } else if (ret > 0) {
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen /* request wasn't fully read */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_error("Auth request missing a file descriptor");
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen i_error("Auth request inode mismatch: %s != %s",
4e56e6408815c04f2e5b904a648a366a2dcbd408Timo Sirainenstatic void master_login_client_free(struct master_login_client **_client)
4e56e6408815c04f2e5b904a648a366a2dcbd408Timo Sirainen struct master_login_client *client = *_client;
e871bfe83e8c5cc7768de30afe0127a3c4373adeTimo Sirainen /* FIXME: currently we create a separate connection for each request,
e871bfe83e8c5cc7768de30afe0127a3c4373adeTimo Sirainen so close the connection after we're done with this client */
e871bfe83e8c5cc7768de30afe0127a3c4373adeTimo Sirainen if (!master_login_conn_is_closed(client->conn))
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainenstatic void master_login_auth_finish(struct master_login_client *client,
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen const char *const *auth_args)
e871bfe83e8c5cc7768de30afe0127a3c4373adeTimo Sirainen struct master_login *login = client->conn->login;
7891c8e6debdcfec552cb1beea2a0230fe89957bTimo Sirainen struct master_service *service = login->service;
7891c8e6debdcfec552cb1beea2a0230fe89957bTimo Sirainen close_sockets = service->master_status.available_count == 0 &&
7891c8e6debdcfec552cb1beea2a0230fe89957bTimo Sirainen login->callback(client, auth_args[0], auth_args+1);
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen /* we're dying as soon as this connection closes. */
7891c8e6debdcfec552cb1beea2a0230fe89957bTimo Sirainen i_assert(master_login_auth_request_count(login->auth) == 0);
7891c8e6debdcfec552cb1beea2a0230fe89957bTimo Sirainen /* try stopping again */
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainenstatic void master_login_postlogin_free(struct master_login_postlogin *pl)
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainenstatic void master_login_postlogin_input(struct master_login_postlogin *pl)
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen struct master_login *login = pl->client->conn->login;
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen const char **auth_args, **p;
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen unsigned int len;
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen while ((ret = fd_read(pl->fd, buf, sizeof(buf), &fd)) > 0) {
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen /* post-login script replaced fd */
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen if (len > 0 && str_c(pl->input)[len-1] == '\n') {
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen /* finished reading the input */
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen auth_args = t_strsplit(str_c(pl->input), "\t");
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen master_login_auth_finish(pl->client, auth_args);
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainenstatic void master_login_postlogin_timeout(struct master_login_postlogin *pl)
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen struct master_login *login = pl->client->conn->login;
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen i_error("%s: Timeout waiting for post-login script to finish, aborting",
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainenstatic int master_login_postlogin(struct master_login_client *client,
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen const char *const *auth_args)
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen struct master_login *login = client->conn->login;
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen unsigned int i;
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen fd = net_connect_unix_with_retries(login->postlogin_socket_path, 1000);
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen str_printfa(str, "%s\t%s", net_ip2addr(&client->auth_req.local_ip),
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen ret = fd_send(fd, client->fd, str_data(str), str_len(str));
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen pl->io = io_add(fd, IO_READ, master_login_postlogin_input, pl);
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen pl->to = timeout_add(MASTER_LOGIN_POSTLOGIN_TIMEOUT_MSECS,
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainenmaster_login_auth_callback(const char *const *auth_args, const char *errormsg,
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen reply.status = errormsg == NULL ? MASTER_AUTH_STATUS_OK :
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen o_stream_send(client->conn->output, &reply, sizeof(reply));
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen if (errormsg != NULL || auth_args[0] == NULL) {
b71f152acb8a197d20b709ca74366e6d765bd200Timo Sirainen i_error("login client: Username missing from auth reply");
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen errormsg = MASTER_AUTH_ERRMSG_INTERNAL_FAILURE;
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen client->conn->login->failure_callback(client, errormsg);
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen if (client->conn->login->postlogin_socket_path == NULL)
1d3b9fce06b466bcf64f9ab7b622f3a6e4e939baTimo Sirainen /* execute post-login scripts before finishing auth */
baca06331782e2752734199486e51a26d7c93d75Timo Sirainen if (master_login_postlogin(client, auth_args) < 0)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenstatic void master_login_conn_input(struct master_login_connection *conn)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen unsigned char data[MASTER_AUTH_MAX_DATA_SIZE];
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen ret = master_login_conn_read_request(conn, &req, data, &client_fd);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen /* @UNSAFE: we have a request. do userdb lookup for it. */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen client = i_malloc(sizeof(struct master_login_client) + req.data_size);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenvoid master_login_add(struct master_login *login, int fd)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn = i_new(struct master_login_connection, 1);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->io = io_add(conn->fd, IO_READ, master_login_conn_input, conn);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
baca06331782e2752734199486e51a26d7c93d75Timo Sirainen /* NOTE: currently there's a separate connection for each request. */
e871bfe83e8c5cc7768de30afe0127a3c4373adeTimo Sirainenstatic void master_login_conn_close(struct master_login_connection *conn)
4e56e6408815c04f2e5b904a648a366a2dcbd408Timo Sirainenstatic void master_login_conn_unref(struct master_login_connection **_conn)
4e56e6408815c04f2e5b904a648a366a2dcbd408Timo Sirainen struct master_login_connection *conn = *_conn;
9ba5c6da815d5d4b43861387dd08fcea321a0423Timo Sirainen master_service_client_connection_destroyed(conn->login->service);
7891c8e6debdcfec552cb1beea2a0230fe89957bTimo Sirainenvoid master_login_stop(struct master_login *login)
7891c8e6debdcfec552cb1beea2a0230fe89957bTimo Sirainen if (master_login_auth_request_count(login->auth) == 0) {