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(&params);
270 	params.client_fd = fd;
271 	params.request = *request;
272 	params.data = data;
273 
274 	master_auth_request_full(auth, &params, 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