2 * Sub-authentication Daemon
4 * (c) 2017 Martin Mares <mj@ucw.cz>
10 #include <ucw/mainloop.h>
12 #include <ucw/trans.h>
13 #include <ucw-json/json.h>
17 #include <sys/socket.h>
23 static char *socket_path = "subauthd.socket";
24 static uint max_connections = ~0U;
26 static struct main_file listen_socket;
27 static uint num_connections;
29 #define SOCKET_TIMEOUT 60000 // in ms
30 #define MAX_PACKET_SIZE 16384
31 #define MAX_OOB_DATA_SIZE 4096
34 struct main_file socket;
35 struct main_timer timer;
37 struct json_context *json;
38 struct json_node *request;
39 struct json_node *reply;
42 static byte packet_buffer[MAX_PACKET_SIZE];
43 static byte oob_data_buffer[MAX_OOB_DATA_SIZE];
45 static int socket_read_handler(struct main_file *fi);
46 static int socket_write_handler(struct main_file *fi);
48 static void client_close(struct client *c)
50 msg(L_INFO, "Closing connection");
59 static void cmd_error(struct client *c, const char *err)
61 json_object_set(c->reply, "error", json_new_string(c->json, err));
64 static void socket_timeout_handler(struct main_timer *tm)
66 struct client *c = tm->data;
68 msg(L_INFO, "Client timeout");
73 static void try_send_reply(struct client *c)
76 fbbuf_init_write(&fb, packet_buffer, MAX_PACKET_SIZE);
80 json_write(c->json, &fb, c->reply);
84 msg(L_ERROR, "Unable to construct reply, it is probably too long");
85 fbbuf_init_write(&fb, packet_buffer, MAX_PACKET_SIZE);
86 bputs(&fb, "{ \"error\": \"Reply too long\" }\n");
90 int len = fbbuf_count_written(&fb);
91 msg(L_INFO, "Sending reply of %d bytes", len);
92 if (send(c->socket.fd, packet_buffer, len, 0) < 0)
94 if (errno == EAGAIN || errno == EINTR)
96 msg(L_INFO, "Postponed send");
97 c->socket.write_handler = socket_write_handler;
100 msg(L_ERROR, "Client write error: %m");
105 msg(L_INFO, "Reply sent");
106 c->socket.read_handler = socket_read_handler;
107 c->socket.write_handler = NULL;
108 file_chg(&c->socket);
109 timer_add_rel(&c->timer, SOCKET_TIMEOUT);
113 static void send_reply(struct client *c)
115 timer_add_rel(&c->timer, SOCKET_TIMEOUT);
119 static void received_packet(struct client *c, byte *pkt, int len)
124 fbbuf_init_read(&fb, pkt, len, 0);
126 c->reply = json_new_object(c->json);
130 c->request = json_parse(c->json, &fb);
134 cmd_error(c, "Parse error");
144 static int socket_write_handler(struct main_file *fi)
146 struct client *c = fi->data;
151 static int socket_read_handler(struct main_file *fi)
153 struct client *c = fi->data;
156 .iov_base = packet_buffer,
157 .iov_len = MAX_PACKET_SIZE,
163 .msg_control = oob_data_buffer,
164 .msg_controllen = MAX_OOB_DATA_SIZE,
167 ssize_t len = recvmsg(fi->fd, &mh, 0);
170 if (errno != EAGAIN && errno != EINTR)
171 msg(L_ERROR, "Socket read: %m");
181 struct ucred *cred = NULL;
182 for (struct cmsghdr *cm = CMSG_FIRSTHDR(&mh); cm; cm = CMSG_NXTHDR(&mh, cm))
184 if (cm->cmsg_level == SOL_SOCKET)
186 if (cm->cmsg_type == SCM_RIGHTS)
188 // We are not interested in receiving file descriptor, but despite
189 // that they could be attached to the message. If it happens, simply
191 int *fdptr = (int *) CMSG_DATA(cm);
192 int nfd = cm->cmsg_len / sizeof(int);
193 for (int i=0; i<nfd; i++)
196 else if (cm->cmsg_type == SCM_CREDENTIALS)
198 ASSERT(cm->cmsg_len >= sizeof(cred));
199 cred = (struct ucred *) CMSG_DATA(cm);
206 msg(L_ERROR, "Dropping message with no credentials");
210 msg(L_INFO, "Got message from UID %d", (int) cred->uid);
213 fi->read_handler = NULL;
216 received_packet(c, packet_buffer, len);
220 static int listen_read_handler(struct main_file *fi)
222 struct sockaddr_un client;
223 socklen_t addr_len = sizeof(client);
225 int new_sk = accept(fi->fd, &client, &addr_len);
228 if (errno != EAGAIN && errno != EINTR)
229 msg(L_ERROR, "Socket accept: %m");
233 if (num_connections >= max_connections)
235 msg(L_WARN, "Too many connections (you might need to increase MaxConnections)");
241 msg(L_INFO, "Accepted connection");
243 if (fcntl(new_sk, F_SETFL, fcntl(new_sk, F_GETFL) | O_NONBLOCK) < 0)
244 die("Cannot set O_NONBLOCK: %m");
246 struct client *c = xmalloc_zero(sizeof(*c));
247 c->json = json_new();
249 c->socket.fd = new_sk;
250 c->socket.read_handler = socket_read_handler;
252 file_add(&c->socket);
254 c->timer.handler = socket_timeout_handler;
256 timer_add_rel(&c->timer, SOCKET_TIMEOUT);
261 static void init_socket(void)
263 int sk = socket(PF_UNIX, SOCK_SEQPACKET, 0);
265 die("socket(PF_UNIX, SOCK_SEQPACKET): %m");
267 if (fcntl(sk, F_SETFL, fcntl(sk, F_GETFL) | O_NONBLOCK) < 0)
268 die("Cannot set O_NONBLOCK: %m");
270 struct sockaddr_un sun;
271 sun.sun_family = AF_UNIX;
272 if (strlen(socket_path) >= sizeof(sun.sun_path))
273 die("SocketPath too long");
274 strcpy(sun.sun_path, socket_path);
276 if (unlink(socket_path) < 0 && errno != ENOENT)
277 die("Cannot unlink old socket %s: %m", socket_path);
279 if (bind(sk, (struct sockaddr *) &sun, sizeof(sun)) < 0)
280 die("Cannot bind to %s: %m", socket_path);
282 if (listen(sk, 64) < 0)
286 if (setsockopt(sk, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
287 die("setsockopt(SO_PASSCRED): %m");
289 listen_socket.fd = sk;
290 listen_socket.read_handler = listen_read_handler;
291 file_add(&listen_socket);
293 msg(L_INFO, "Listening on %s", socket_path);
296 static struct cf_section daemon_config = {
298 CF_STRING("SocketPath", &socket_path),
299 CF_UINT("MaxConnections", &max_connections),
304 static const struct opt_section options = {
306 OPT_HELP("A sub-authentication daemon."),
307 OPT_HELP("Usage: subauthd [options]"),
309 OPT_HELP("Options:"),
316 int main(int argc UNUSED, char **argv)
318 cf_def_file = CONFIG_DIR "/subauthd";
319 cf_declare_section("SubauthD", &daemon_config, 0);
320 opt_parse(&options, argv+1);