]> mj.ucw.cz Git - subauth.git/blob - server/subauthd.c
Server: Maximum comment size is limited (configurable)
[subauth.git] / server / subauthd.c
1 /*
2  *      Sub-authentication Daemon
3  *
4  *      (c) 2017 Martin Mares <mj@ucw.cz>
5  */
6
7 #undef LOCAL_DEBUG
8
9 #include <ucw/lib.h>
10 #include <ucw/conf.h>
11 #include <ucw/log.h>
12 #include <ucw/opt.h>
13 #include <ucw/trans.h>
14
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <sys/socket.h>
18 #include <sys/stat.h>
19 #include <sys/un.h>
20 #include <unistd.h>
21
22 #include "subauthd.h"
23
24 // Configuration
25 static char *socket_path = "subauthd.socket";
26 static uint max_connections = ~0U;
27 clist zone_list;
28 char *database_name = "subauthd.db";
29 char *temp_key_file;
30 char *log_stream_name;
31 static uint max_packet_size = 16384;
32 uint max_comment_size = 100;
33
34 static struct main_file listen_socket;
35 static uint num_connections;
36
37 static byte *packet_buffer;
38 static byte oob_data_buffer[MAX_OOB_DATA_SIZE];
39
40 static int socket_read_handler(struct main_file *fi);
41 static int socket_write_handler(struct main_file *fi);
42
43 static void client_close(struct client *c)
44 {
45   DBG("Closing connection");
46   file_del(&c->socket);
47   timer_del(&c->timer);
48   close(c->socket.fd);
49   json_delete(c->json);
50   mp_delete(c->pool);           // This includes the connection structure
51   num_connections--;
52 }
53
54 static void socket_timeout_handler(struct main_timer *tm)
55 {
56   struct client *c = tm->data;
57   msg(L_INFO, "Client timeout");
58   client_close(c);
59 }
60
61 static void try_send_reply(struct client *c)
62 {
63   int len;
64
65   TRANS_TRY
66     {
67       struct fastbuf fb;
68       fbbuf_init_write(&fb, packet_buffer, max_packet_size);
69       fb_tie(&fb);
70       json_write(c->json, &fb, c->reply);
71       len = fbbuf_count_written(&fb);
72     }
73   TRANS_CATCH(x)
74     {
75       msg(L_ERROR, "Unable to construct reply, it is probably too long");
76       struct fastbuf fb2;
77       fbbuf_init_write(&fb2, packet_buffer, max_packet_size);
78       bputs(&fb2, "{ \"error\": \"Reply too long\" }\n");
79       len = fbbuf_count_written(&fb2);
80     }
81   TRANS_END;
82
83   DBG("Sending reply of %d bytes", len);
84   if (send(c->socket.fd, packet_buffer, len, 0) < 0)
85     {
86       if (errno == EAGAIN || errno == EINTR)
87         {
88           DBG("Postponed send");
89           c->socket.write_handler = socket_write_handler;
90           file_chg(&c->socket);
91         }
92       msg(L_ERROR, "Client write error: %m");
93       client_close(c);
94     }
95   else
96     {
97       DBG("Reply sent");
98       c->socket.read_handler = socket_read_handler;
99       c->socket.write_handler = NULL;
100       file_chg(&c->socket);
101       timer_add_rel(&c->timer, SOCKET_TIMEOUT);
102     }
103 }
104
105 static void send_reply(struct client *c)
106 {
107   timer_add_rel(&c->timer, SOCKET_TIMEOUT);
108   try_send_reply(c);
109 }
110
111 static void received_packet(struct client *c, byte *pkt, int len)
112 {
113   json_reset(c->json);
114
115   struct fastbuf fb;
116   fbbuf_init_read(&fb, pkt, len, 0);
117
118   c->reply = json_new_object(c->json);
119
120   TRANS_TRY
121     {
122       c->request = json_parse(c->json, &fb);
123     }
124   TRANS_CATCH(x)
125     {
126       json_object_set(c->reply, "error", json_new_string_ref(c->json, "Parse error"));
127       send_reply(c);
128       return;
129     }
130   TRANS_END;
131
132   cmd_dispatch(c);
133   send_reply(c);
134 }
135
136 static int socket_write_handler(struct main_file *fi)
137 {
138   struct client *c = fi->data;
139   try_send_reply(c);
140   return HOOK_IDLE;
141 }
142
143 static int socket_read_handler(struct main_file *fi)
144 {
145   struct client *c = fi->data;
146
147   struct iovec iov = {
148     .iov_base = packet_buffer,
149     .iov_len = max_packet_size,
150   };
151
152   struct msghdr mh = {
153     .msg_iov = &iov,
154     .msg_iovlen = 1,
155     .msg_control = oob_data_buffer,
156     .msg_controllen = MAX_OOB_DATA_SIZE,
157   };
158
159   ssize_t len = recvmsg(fi->fd, &mh, 0);
160   if (len < 0)
161     {
162       if (errno != EAGAIN && errno != EINTR)
163         msg(L_ERROR, "Socket read: %m");
164       return HOOK_IDLE;
165     }
166
167   if (!len)
168     {
169       client_close(c);
170       return HOOK_IDLE;
171     }
172
173   struct ucred *cred = NULL;
174   for (struct cmsghdr *cm = CMSG_FIRSTHDR(&mh); cm; cm = CMSG_NXTHDR(&mh, cm))
175     {
176       if (cm->cmsg_level == SOL_SOCKET)
177         {
178           if (cm->cmsg_type == SCM_RIGHTS)
179             {
180               // We are not interested in receiving file descriptor, but despite
181               // that they could be attached to the message. If it happens, simply
182               // close them.
183               int *fdptr = (int *) CMSG_DATA(cm);
184               int nfd = cm->cmsg_len / sizeof(int);
185               for (int i=0; i<nfd; i++)
186                 close(fdptr[i]);
187             }
188           else if (cm->cmsg_type == SCM_CREDENTIALS)
189             {
190               ASSERT(cm->cmsg_len >= sizeof(cred));
191               cred = (struct ucred *) CMSG_DATA(cm);
192             }
193         }
194     }
195
196   if (!cred)
197     {
198       msg(L_ERROR, "Dropping message with no credentials");
199       return HOOK_RETRY;
200     }
201
202   DBG("Got message from UID %d", (int) cred->uid);
203   c->uid = cred->uid;
204
205   fi->read_handler = NULL;
206   file_chg(fi);
207
208   received_packet(c, packet_buffer, len);
209   return HOOK_RETRY;
210 }
211
212 static int listen_read_handler(struct main_file *fi)
213 {
214   struct sockaddr_un client;
215   socklen_t addr_len = sizeof(client);
216
217   int new_sk = accept(fi->fd, &client, &addr_len);
218   if (new_sk < 0)
219     {
220       if (errno != EAGAIN && errno != EINTR)
221         msg(L_ERROR, "Socket accept: %m");
222       return HOOK_IDLE;
223     }
224
225   if (num_connections >= max_connections)
226     {
227       msg(L_WARN, "Too many connections (you might need to increase MaxConnections)");
228       close(new_sk);
229       return HOOK_IDLE;
230     }
231   num_connections++;
232
233   DBG("Accepted connection");
234
235   if (fcntl(new_sk, F_SETFL, fcntl(new_sk, F_GETFL) | O_NONBLOCK) < 0)
236     die("Cannot set O_NONBLOCK: %m");
237
238   struct mempool *mp = mp_new(4096);
239   struct client *c = mp_alloc_zero(mp, sizeof(*c));
240   c->pool = mp;
241   c->json = json_new();
242
243   c->socket.fd = new_sk;
244   c->socket.read_handler = socket_read_handler;
245   c->socket.data = c;
246   file_add(&c->socket);
247
248   c->timer.handler = socket_timeout_handler;
249   c->timer.data = c;
250   timer_add_rel(&c->timer, SOCKET_TIMEOUT);
251
252   return HOOK_RETRY;
253 }
254
255 static void init_socket(void)
256 {
257   packet_buffer = xmalloc(max_packet_size);
258
259   int sk = socket(PF_UNIX, SOCK_SEQPACKET, 0);
260   if (sk < 0)
261     die("socket(PF_UNIX, SOCK_SEQPACKET): %m");
262
263   if (fcntl(sk, F_SETFL, fcntl(sk, F_GETFL) | O_NONBLOCK) < 0)
264     die("Cannot set O_NONBLOCK: %m");
265
266   struct sockaddr_un sun;
267   sun.sun_family = AF_UNIX;
268   if (strlen(socket_path) >= sizeof(sun.sun_path))
269     die("SocketPath too long");
270   strcpy(sun.sun_path, socket_path);
271
272   if (unlink(socket_path) < 0 && errno != ENOENT)
273     die("Cannot unlink old socket %s: %m", socket_path);
274
275   if (bind(sk, (struct sockaddr *) &sun, sizeof(sun)) < 0)
276     die("Cannot bind to %s: %m", socket_path);
277
278   if (listen(sk, 64) < 0)
279     die("listen(): %m");
280
281   int one = 1;
282   if (setsockopt(sk, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
283     die("setsockopt(SO_PASSCRED): %m");
284
285   listen_socket.fd = sk;
286   listen_socket.read_handler = listen_read_handler;
287   file_add(&listen_socket);
288
289   if (chmod(socket_path, 0666) < 0)
290     die("Cannot chmod socket: %m");
291
292   msg(L_INFO, "Listening on %s", socket_path);
293 }
294
295 static char *zone_commit(void *z_)
296 {
297   struct auth_zone *z = z_;
298   if (!z->name)
299     return "A zone must have a name";
300   return NULL;
301 }
302
303 static struct cf_section zone_config = {
304   CF_TYPE(struct auth_zone),
305   CF_COMMIT(zone_commit),
306   CF_ITEMS {
307     CF_STRING("Name", PTR_TO(struct auth_zone, name)),
308     CF_STRING("Description", PTR_TO(struct auth_zone, desc)),
309     CF_UINT("AutoCreateAcct", PTR_TO(struct auth_zone, auto_create_acct)),
310     CF_UINT("AllowPasswd", PTR_TO(struct auth_zone, allow_passwd)),
311     CF_UINT("AllowTokens", PTR_TO(struct auth_zone, allow_tokens)),
312     CF_UINT("MaxTempValidity", PTR_TO(struct auth_zone, max_temp_validity)),
313     CF_END
314   }
315 };
316
317 static struct cf_section daemon_config = {
318   CF_ITEMS {
319     CF_STRING("SocketPath", &socket_path),
320     CF_UINT("MaxConnections", &max_connections),
321     CF_UINT("MaxPacketSize", &max_packet_size),
322     CF_UINT("MaxCommentSize", &max_comment_size),
323     CF_LIST("Zone", &zone_list, &zone_config),
324     CF_STRING("Database", &database_name),
325     CF_STRING("TempKeyFile", &temp_key_file),
326     CF_STRING("LogStream", &log_stream_name),
327     CF_END
328   }
329 };
330
331 static const struct opt_section options = {
332   OPT_ITEMS {
333     OPT_HELP("A sub-authentication daemon."),
334     OPT_HELP("Usage: subauthd [options]"),
335     OPT_HELP(""),
336     OPT_HELP("Options:"),
337     OPT_HELP_OPTION,
338     OPT_CONF_OPTIONS,
339     OPT_END
340   }
341 };
342
343 int main(int argc UNUSED, char **argv)
344 {
345   umask(0077);
346
347   cf_def_file = CONFIG_DIR "/subauthd";
348   cf_declare_section("SubauthD", &daemon_config, 0);
349   opt_parse(&options, argv+1);
350
351   if (log_stream_name)
352     log_configured(log_stream_name);
353
354   auth_init();
355   temp_init();
356   main_init();
357   init_socket();
358
359   main_loop();
360   return 0;
361 }