1 /* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "ioloop.h"
5 #include "fdpass.h"
6 #include "buffer.h"
7 #include "hash.h"
8 #include "time-util.h"
9 #include "master-service-private.h"
10 #include "master-auth.h"
11
12 #include <unistd.h>
13 #include <sys/stat.h>
14
15 #define SOCKET_CONNECT_RETRY_MSECS 500
16 #define SOCKET_CONNECT_RETRY_WARNING_INTERVAL_SECS 2
17 #define MASTER_AUTH_REQUEST_TIMEOUT_MSECS (MASTER_LOGIN_TIMEOUT_SECS/2*1000)
18
19 struct master_auth_connection {
20 struct master_auth *auth;
21 unsigned int tag;
22
23 unsigned int client_pid, auth_id;
24 struct ip_addr remote_ip;
25 struct timeval create_time;
26
27 char *path;
28 int fd;
29 struct io *io;
30 struct timeout *to;
31
32 char buf[sizeof(struct master_auth_reply)];
33 unsigned int buf_pos;
34
35 master_auth_callback_t *callback;
36 void *context;
37 };
38
39 struct master_auth {
40 struct master_service *service;
41 pool_t pool;
42
43 const char *default_path;
44 time_t last_connect_warning;
45
46 unsigned int tag_counter;
47 HASH_TABLE(void *, struct master_auth_connection *) connections;
48 };
49
50 struct master_auth *
master_auth_init(struct master_service * service,const char * path)51 master_auth_init(struct master_service *service, const char *path)
52 {
53 struct master_auth *auth;
54 pool_t pool;
55
56 pool = pool_alloconly_create("master auth", 1024);
57 auth = p_new(pool, struct master_auth, 1);
58 auth->pool = pool;
59 auth->service = service;
60 auth->default_path = p_strdup(pool, path);
61 hash_table_create_direct(&auth->connections, pool, 0);
62 return auth;
63 }
64
65 static void
master_auth_connection_deinit(struct master_auth_connection ** _conn)66 master_auth_connection_deinit(struct master_auth_connection **_conn)
67 {
68 struct master_auth_connection *conn = *_conn;
69
70 *_conn = NULL;
71
72 if (conn->tag != 0)
73 hash_table_remove(conn->auth->connections,
74 POINTER_CAST(conn->tag));
75
76 if (conn->callback != NULL)
77 conn->callback(NULL, conn->context);
78
79 timeout_remove(&conn->to);
80 io_remove(&conn->io);
81 if (conn->fd != -1) {
82 if (close(conn->fd) < 0)
83 i_fatal("close(%s) failed: %m", conn->path);
84 conn->fd = -1;
85 }
86 i_free(conn->path);
87 i_free(conn);
88 }
89
90 static void ATTR_FORMAT(2, 3)
conn_error(struct master_auth_connection * conn,const char * fmt,...)91 conn_error(struct master_auth_connection *conn, const char *fmt, ...)
92 {
93 va_list args;
94
95 va_start(args, fmt);
96 i_error("master(%s): %s (client-pid=%u, client-id=%u, rip=%s, created %u msecs ago, received %u/%zu bytes)",
97 conn->path, t_strdup_vprintf(fmt, args),
98 conn->client_pid, conn->auth_id, net_ip2addr(&conn->remote_ip),
99 timeval_diff_msecs(&ioloop_timeval, &conn->create_time),
100 conn->buf_pos, sizeof(conn->buf_pos));
101 va_end(args);
102 }
103
master_auth_deinit(struct master_auth ** _auth)104 void master_auth_deinit(struct master_auth **_auth)
105 {
106 struct master_auth *auth = *_auth;
107 struct hash_iterate_context *iter;
108 void *key;
109 struct master_auth_connection *conn;
110
111 *_auth = NULL;
112
113 iter = hash_table_iterate_init(auth->connections);
114 while (hash_table_iterate(iter, auth->connections, &key, &conn)) {
115 conn->tag = 0;
116 master_auth_connection_deinit(&conn);
117 }
118 hash_table_iterate_deinit(&iter);
119 hash_table_destroy(&auth->connections);
120 pool_unref(&auth->pool);
121 }
122
master_auth_connection_input(struct master_auth_connection * conn)123 static void master_auth_connection_input(struct master_auth_connection *conn)
124 {
125 const struct master_auth_reply *reply;
126 int ret;
127
128 ret = read(conn->fd, conn->buf + conn->buf_pos,
129 sizeof(conn->buf) - conn->buf_pos);
130 if (ret <= 0) {
131 if (ret == 0 || errno == ECONNRESET) {
132 conn_error(conn, "read() failed: Remote closed connection "
133 "(destination service { process_limit } reached?)");
134 } else {
135 if (errno == EAGAIN)
136 return;
137 conn_error(conn, "read() failed: %m");
138 }
139 master_auth_connection_deinit(&conn);
140 return;
141 }
142
143 conn->buf_pos += ret;
144 if (conn->buf_pos < sizeof(conn->buf))
145 return;
146
147 /* reply is now read */
148 reply = (const void *)conn->buf;
149 conn->buf_pos = 0;
150
151 if (conn->tag != reply->tag)
152 conn_error(conn, "Received reply with unknown tag %u", reply->tag);
153 else if (conn->callback == NULL) {
154 /* request aborted */
155 } else {
156 conn->callback(reply, conn->context);
157 conn->callback = NULL;
158 }
159 master_auth_connection_deinit(&conn);
160 }
161
master_auth_connection_timeout(struct master_auth_connection * conn)162 static void master_auth_connection_timeout(struct master_auth_connection *conn)
163 {
164 conn_error(conn, "Auth request timed out");
165 master_auth_connection_deinit(&conn);
166 }
167
master_auth_request_full(struct master_auth * auth,const struct master_auth_request_params * params,master_auth_callback_t * callback,void * context,unsigned int * tag_r)168 void master_auth_request_full(struct master_auth *auth,
169 const struct master_auth_request_params *params,
170 master_auth_callback_t *callback, void *context,
171 unsigned int *tag_r)
172 {
173 struct master_auth_connection *conn;
174 struct master_auth_request req;
175 buffer_t *buf;
176 struct stat st;
177 ssize_t ret;
178
179 i_assert(params->request.client_pid != 0);
180 i_assert(params->request.auth_pid != 0);
181
182 conn = i_new(struct master_auth_connection, 1);
183 conn->auth = auth;
184 conn->create_time = ioloop_timeval;
185 conn->callback = callback;
186 conn->context = context;
187 conn->path = params->socket_path != NULL ?
188 i_strdup(params->socket_path) : i_strdup(auth->default_path);
189
190 req = params->request;
191 req.tag = ++auth->tag_counter;
192 if (req.tag == 0)
193 req.tag = ++auth->tag_counter;
194
195 conn->client_pid = req.client_pid;
196 conn->auth_id = req.auth_id;
197 conn->remote_ip = req.remote_ip;
198
199 if (fstat(params->client_fd, &st) < 0)
200 i_fatal("fstat(auth dest fd) failed: %m");
201 req.ino = st.st_ino;
202
203 buf = t_buffer_create(sizeof(req) + req.data_size);
204 buffer_append(buf, &req, sizeof(req));
205 buffer_append(buf, params->data, req.data_size);
206
207 conn->fd = net_connect_unix(conn->path);
208 if (conn->fd == -1 && errno == EAGAIN) {
209 /* Couldn't connect to the socket immediately. This will add
210 a delay that causes hangs to the whole process, which won't
211 be obvious unless we log a warning. FIXME: The wait could
212 be asynchronous. */
213 struct timeval start_time;
214
215 io_loop_time_refresh();
216 start_time = ioloop_timeval;
217 conn->fd = net_connect_unix_with_retries(conn->path,
218 SOCKET_CONNECT_RETRY_MSECS);
219 io_loop_time_refresh();
220 if (conn->fd != -1 &&
221 ioloop_time - auth->last_connect_warning >=
222 SOCKET_CONNECT_RETRY_WARNING_INTERVAL_SECS) {
223 i_warning("net_connect_unix(%s) succeeded only after retrying - "
224 "took %lld us", conn->path,
225 timeval_diff_usecs(&ioloop_timeval, &start_time));
226 auth->last_connect_warning = ioloop_time;
227 }
228 }
229 if (conn->fd == -1) {
230 conn_error(conn, "net_connect_unix(%s) failed: %m%s",
231 conn->path, errno != EAGAIN ? "" :
232 " - http://wiki2.dovecot.org/SocketUnavailable");
233 master_auth_connection_deinit(&conn);
234 return;
235 }
236
237 ret = fd_send(conn->fd, params->client_fd, buf->data, buf->used);
238 if (ret < 0) {
239 conn_error(conn, "fd_send(fd=%d) failed: %m",
240 params->client_fd);
241 } else if ((size_t)ret != buf->used) {
242 conn_error(conn, "fd_send() sent only %d of %d bytes",
243 (int)ret, (int)buf->used);
244 ret = -1;
245 }
246 if (ret < 0) {
247 master_auth_connection_deinit(&conn);
248 return;
249 }
250
251 conn->tag = req.tag;
252 conn->to = timeout_add(MASTER_AUTH_REQUEST_TIMEOUT_MSECS,
253 master_auth_connection_timeout, conn);
254 conn->io = io_add(conn->fd, IO_READ,
255 master_auth_connection_input, conn);
256 i_assert(hash_table_lookup(auth->connections, POINTER_CAST(req.tag)) == NULL);
257 hash_table_insert(auth->connections, POINTER_CAST(req.tag), conn);
258 *tag_r = req.tag;
259 }
260
master_auth_request(struct master_auth * auth,int fd,const struct master_auth_request * request,const unsigned char * data,master_auth_callback_t * callback,void * context,unsigned int * tag_r)261 void master_auth_request(struct master_auth *auth, int fd,
262 const struct master_auth_request *request,
263 const unsigned char *data,
264 master_auth_callback_t *callback,
265 void *context, unsigned int *tag_r)
266 {
267 struct master_auth_request_params params;
268
269 i_zero(¶ms);
270 params.client_fd = fd;
271 params.request = *request;
272 params.data = data;
273
274 master_auth_request_full(auth, ¶ms, callback, context, tag_r);
275 }
276
master_auth_request_abort(struct master_auth * auth,unsigned int tag)277 void master_auth_request_abort(struct master_auth *auth, unsigned int tag)
278 {
279 struct master_auth_connection *conn;
280
281 conn = hash_table_lookup(auth->connections, POINTER_CAST(tag));
282 if (conn == NULL)
283 i_panic("master_auth_request_abort(): tag %u not found", tag);
284
285 conn->callback = NULL;
286 }
287