2 * Sub-authentication Daemon
4 * (c) 2017 Martin Mares <mj@ucw.cz>
13 #include <ucw/trans.h>
18 #include <sys/socket.h>
26 static char *socket_path = "subauthd.socket";
27 static uint max_connections = ~0U;
29 char *database_name = "subauthd.db";
31 char *log_stream_name;
32 static uint max_packet_size = 16384;
33 uint max_comment_size = 100;
35 static struct main_file listen_socket;
36 static uint num_connections;
38 static byte *packet_buffer;
39 static byte oob_data_buffer[MAX_OOB_DATA_SIZE];
41 static int socket_read_handler(struct main_file *fi);
42 static int socket_write_handler(struct main_file *fi);
44 static void client_close(struct client *c)
46 DBG("Closing connection");
51 mp_delete(c->pool); // This includes the connection structure
55 static void socket_timeout_handler(struct main_timer *tm)
57 struct client *c = tm->data;
58 msg(L_INFO, "Client timeout");
62 static void try_send_reply(struct client *c)
69 fbbuf_init_write(&fb, packet_buffer, max_packet_size);
71 json_write(c->json, &fb, c->reply);
72 len = fbbuf_count_written(&fb);
76 msg(L_ERROR, "Unable to construct reply, it is probably too long");
78 fbbuf_init_write(&fb2, packet_buffer, max_packet_size);
79 bputs(&fb2, "{ \"error\": \"Reply too long\" }\n");
80 len = fbbuf_count_written(&fb2);
84 DBG("Sending reply of %d bytes", len);
85 if (send(c->socket.fd, packet_buffer, len, 0) < 0)
87 if (errno == EAGAIN || errno == EINTR)
89 DBG("Postponed send");
90 c->socket.write_handler = socket_write_handler;
93 msg(L_ERROR, "Client write error: %m");
99 c->socket.read_handler = socket_read_handler;
100 c->socket.write_handler = NULL;
101 file_chg(&c->socket);
102 timer_add_rel(&c->timer, SOCKET_TIMEOUT);
106 static void send_reply(struct client *c)
108 timer_add_rel(&c->timer, SOCKET_TIMEOUT);
112 static void received_packet(struct client *c, byte *pkt, int len)
117 fbbuf_init_read(&fb, pkt, len, 0);
119 c->reply = json_new_object(c->json);
123 c->request = json_parse(c->json, &fb);
127 json_object_set(c->reply, "error", json_new_string_ref(c->json, "Parse error"));
137 static int socket_write_handler(struct main_file *fi)
139 struct client *c = fi->data;
144 static int socket_read_handler(struct main_file *fi)
146 struct client *c = fi->data;
149 .iov_base = packet_buffer,
150 .iov_len = max_packet_size,
156 .msg_control = oob_data_buffer,
157 .msg_controllen = MAX_OOB_DATA_SIZE,
160 ssize_t len = recvmsg(fi->fd, &mh, 0);
163 if (errno != EAGAIN && errno != EINTR)
164 msg(L_ERROR, "Socket read: %m");
174 struct ucred *cred = NULL;
175 for (struct cmsghdr *cm = CMSG_FIRSTHDR(&mh); cm; cm = CMSG_NXTHDR(&mh, cm))
177 if (cm->cmsg_level == SOL_SOCKET)
179 if (cm->cmsg_type == SCM_RIGHTS)
181 // We are not interested in receiving file descriptor, but despite
182 // that they could be attached to the message. If it happens, simply
184 int *fdptr = (int *) CMSG_DATA(cm);
185 int nfd = cm->cmsg_len / sizeof(int);
186 for (int i=0; i<nfd; i++)
189 else if (cm->cmsg_type == SCM_CREDENTIALS)
191 ASSERT(cm->cmsg_len >= sizeof(cred));
192 cred = (struct ucred *) CMSG_DATA(cm);
199 msg(L_ERROR, "Dropping message with no credentials");
203 DBG("Got message from UID %d", (int) cred->uid);
206 fi->read_handler = NULL;
209 received_packet(c, packet_buffer, len);
213 static int listen_read_handler(struct main_file *fi)
215 struct sockaddr_un client;
216 socklen_t addr_len = sizeof(client);
218 int new_sk = accept(fi->fd, &client, &addr_len);
221 if (errno != EAGAIN && errno != EINTR)
222 msg(L_ERROR, "Socket accept: %m");
226 if (num_connections >= max_connections)
228 msg(L_WARN, "Too many connections (you might need to increase MaxConnections)");
234 DBG("Accepted connection");
236 if (fcntl(new_sk, F_SETFL, fcntl(new_sk, F_GETFL) | O_NONBLOCK) < 0)
237 die("Cannot set O_NONBLOCK: %m");
239 struct mempool *mp = mp_new(4096);
240 struct client *c = mp_alloc_zero(mp, sizeof(*c));
242 c->json = json_new();
244 c->socket.fd = new_sk;
245 c->socket.read_handler = socket_read_handler;
247 file_add(&c->socket);
249 c->timer.handler = socket_timeout_handler;
251 timer_add_rel(&c->timer, SOCKET_TIMEOUT);
256 static void init_socket(void)
258 packet_buffer = xmalloc(max_packet_size);
260 int sk = socket(PF_UNIX, SOCK_SEQPACKET, 0);
262 die("socket(PF_UNIX, SOCK_SEQPACKET): %m");
264 if (fcntl(sk, F_SETFL, fcntl(sk, F_GETFL) | O_NONBLOCK) < 0)
265 die("Cannot set O_NONBLOCK: %m");
267 struct sockaddr_un sun;
268 sun.sun_family = AF_UNIX;
269 if (strlen(socket_path) >= sizeof(sun.sun_path))
270 die("SocketPath too long");
271 strcpy(sun.sun_path, socket_path);
273 if (unlink(socket_path) < 0 && errno != ENOENT)
274 die("Cannot unlink old socket %s: %m", socket_path);
276 if (bind(sk, (struct sockaddr *) &sun, sizeof(sun)) < 0)
277 die("Cannot bind to %s: %m", socket_path);
279 if (listen(sk, 64) < 0)
283 if (setsockopt(sk, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
284 die("setsockopt(SO_PASSCRED): %m");
286 listen_socket.fd = sk;
287 listen_socket.read_handler = listen_read_handler;
288 file_add(&listen_socket);
290 if (chmod(socket_path, 0666) < 0)
291 die("Cannot chmod socket: %m");
293 msg(L_INFO, "Listening on %s", socket_path);
296 static void sig_term(struct main_signal *ms UNUSED)
298 msg(L_INFO, "Shutting down");
302 static void init_signals(void)
304 static struct main_signal ms_sigterm = {
308 signal_add(&ms_sigterm);
311 static char *zone_commit(void *z_)
313 struct auth_zone *z = z_;
315 return "A zone must have a name";
319 static struct cf_section zone_config = {
320 CF_TYPE(struct auth_zone),
321 CF_COMMIT(zone_commit),
323 CF_STRING("Name", PTR_TO(struct auth_zone, name)),
324 CF_STRING("Description", PTR_TO(struct auth_zone, desc)),
325 CF_UINT("AutoCreateAcct", PTR_TO(struct auth_zone, auto_create_acct)),
326 CF_UINT("AllowPasswd", PTR_TO(struct auth_zone, allow_passwd)),
327 CF_UINT("AllowTokens", PTR_TO(struct auth_zone, allow_tokens)),
328 CF_UINT("MaxTempValidity", PTR_TO(struct auth_zone, max_temp_validity)),
333 static struct cf_section daemon_config = {
335 CF_STRING("SocketPath", &socket_path),
336 CF_UINT("MaxConnections", &max_connections),
337 CF_UINT("MaxPacketSize", &max_packet_size),
338 CF_UINT("MaxCommentSize", &max_comment_size),
339 CF_LIST("Zone", &zone_list, &zone_config),
340 CF_STRING("Database", &database_name),
341 CF_STRING("TempKeyFile", &temp_key_file),
342 CF_STRING("LogStream", &log_stream_name),
347 static const struct opt_section options = {
349 OPT_HELP("A sub-authentication daemon."),
350 OPT_HELP("Usage: subauthd [options]"),
352 OPT_HELP("Options:"),
359 int main(int argc UNUSED, char **argv)
363 cf_def_file = CONFIG_DIR "/subauthd";
364 cf_declare_section("SubauthD", &daemon_config, 0);
365 opt_parse(&options, argv+1);
368 log_configured(log_stream_name);