]> mj.ucw.cz Git - subauth.git/blob - subauthd.c
d8ee471fb5c4d6a8a01376a20e398a375517d1df
[subauth.git] / subauthd.c
1 /*
2  *      Sub-authentication Daemon
3  *
4  *      (c) 2017 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <ucw/lib.h>
8 #include <ucw/conf.h>
9 #include <ucw/log.h>
10 #include <ucw/mainloop.h>
11 #include <ucw/opt.h>
12
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <sys/socket.h>
16 #include <sys/un.h>
17 #include <unistd.h>
18
19 #include "autoconf.h"
20
21 static char *socket_path = "subauthd.socket";
22 static uint max_connections = ~0U;
23
24 static struct main_file listen_socket;
25 static uint num_connections;
26
27 #define SOCKET_TIMEOUT 60000            // in ms
28 #define MAX_PACKET_SIZE 4096
29 #define MAX_OOB_DATA_SIZE 4096
30
31 struct client {
32   struct main_file socket;
33   struct main_timer timer;
34 };
35
36 static byte packet_buffer[MAX_PACKET_SIZE];
37 static byte oob_data_buffer[MAX_OOB_DATA_SIZE];
38
39 static void client_close(struct client *c)
40 {
41   msg(L_INFO, "Closing connection");
42   file_del(&c->socket);
43   timer_del(&c->timer);
44   close(c->socket.fd);
45 }
46
47 static void socket_timeout_handler(struct main_timer *tm)
48 {
49   struct client *c = tm->data;
50
51   msg(L_INFO, "Client timeout");
52
53   client_close(c);
54 }
55
56 static int socket_read_handler(struct main_file *fi)
57 {
58   struct client *c = fi->data;
59
60   struct iovec iov = {
61     .iov_base = packet_buffer,
62     .iov_len = MAX_PACKET_SIZE,
63   };
64
65   struct msghdr mh = {
66     .msg_iov = &iov,
67     .msg_iovlen = 1,
68     .msg_control = oob_data_buffer,
69     .msg_controllen = MAX_OOB_DATA_SIZE,
70   };
71
72   ssize_t len = recvmsg(fi->fd, &mh, 0);
73   if (len < 0)
74     {
75       if (errno != EAGAIN && errno != EINTR)
76         msg(L_ERROR, "Socket read: %m");
77       return HOOK_IDLE;
78     }
79
80   if (!len)
81     {
82       client_close(c);
83       return HOOK_IDLE;
84     }
85
86   msg(L_INFO, "Got packet: len=%zd", len);
87
88   struct ucred *cred = NULL;
89   for (struct cmsghdr *cm = CMSG_FIRSTHDR(&mh); cm; cm = CMSG_NXTHDR(&mh, cm))
90     {
91       if (cm->cmsg_level == SOL_SOCKET)
92         {
93           if (cm->cmsg_type == SCM_RIGHTS)
94             {
95               // We are not interested in receiving file descriptor, but despite
96               // that they could be attached to the message. If it happens, simply
97               // close them.
98               int *fdptr = (int *) CMSG_DATA(cm);
99               int nfd = cm->cmsg_len / sizeof(int);
100               for (int i=0; i<nfd; i++)
101                 close(fdptr[i]);
102             }
103           else if (cm->cmsg_type == SCM_CREDENTIALS)
104             {
105               ASSERT(cm->cmsg_len >= sizeof(cred));
106               cred = (struct ucred *) CMSG_DATA(cm);
107             }
108         }
109     }
110
111   if (!cred)
112     {
113       msg(L_ERROR, "Dropping message with no credentials");
114       return HOOK_RETRY;
115     }
116
117   msg(L_INFO, "Credentials: pid=%d uid=%d gid=%d", (int) cred->pid, (int) cred->uid, (int) cred->gid);
118
119   return HOOK_RETRY;
120 }
121
122 static int listen_read_handler(struct main_file *fi)
123 {
124   struct sockaddr_un client;
125   socklen_t addr_len = sizeof(client);
126
127   int new_sk = accept(fi->fd, &client, &addr_len);
128   if (new_sk < 0)
129     {
130       if (errno != EAGAIN && errno != EINTR)
131         msg(L_ERROR, "Socket accept: %m");
132       return HOOK_IDLE;
133     }
134
135   if (num_connections >= max_connections)
136     {
137       msg(L_WARN, "Too many connections (you might need to increase MaxConnections)");
138       close(new_sk);
139       return HOOK_IDLE;
140     }
141   num_connections++;
142
143   msg(L_INFO, "Accepted connection");
144
145   if (fcntl(new_sk, F_SETFL, fcntl(new_sk, F_GETFL) | O_NONBLOCK) < 0)
146     die("Cannot set O_NONBLOCK: %m");
147
148   struct client *c = xmalloc_zero(sizeof(*c));
149   c->socket.fd = new_sk;
150   c->socket.read_handler = socket_read_handler;
151   c->socket.data = c;
152   file_add(&c->socket);
153
154   c->timer.handler = socket_timeout_handler;
155   c->timer.data = c;
156   timer_add_rel(&c->timer, SOCKET_TIMEOUT);
157
158   return HOOK_RETRY;
159 }
160
161 static void init_socket(void)
162 {
163   int sk = socket(PF_UNIX, SOCK_SEQPACKET, 0);
164   if (sk < 0)
165     die("socket(PF_UNIX, SOCK_SEQPACKET): %m");
166
167   if (fcntl(sk, F_SETFL, fcntl(sk, F_GETFL) | O_NONBLOCK) < 0)
168     die("Cannot set O_NONBLOCK: %m");
169
170   struct sockaddr_un sun;
171   sun.sun_family = AF_UNIX;
172   if (strlen(socket_path) >= sizeof(sun.sun_path))
173     die("SocketPath too long");
174   strcpy(sun.sun_path, socket_path);
175
176   if (unlink(socket_path) < 0 && errno != ENOENT)
177     die("Cannot unlink old socket %s: %m", socket_path);
178
179   if (bind(sk, (struct sockaddr *) &sun, sizeof(sun)) < 0)
180     die("Cannot bind to %s: %m", socket_path);
181
182   if (listen(sk, 64) < 0)
183     die("listen(): %m");
184
185   int one;
186   if (setsockopt(sk, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
187     die("setsockopt(SO_PASSCRED): %m");
188
189   listen_socket.fd = sk;
190   listen_socket.read_handler = listen_read_handler;
191   file_add(&listen_socket);
192
193   msg(L_INFO, "Listening on %s", socket_path);
194 }
195
196 static struct cf_section daemon_config = {
197   CF_ITEMS {
198     CF_STRING("SocketPath", &socket_path),
199     CF_UINT("MaxConnections", &max_connections),
200     CF_END
201   }
202 };
203
204 static const struct opt_section options = {
205   OPT_ITEMS {
206     OPT_HELP("A sub-authentication daemon."),
207     OPT_HELP("Usage: subauthd [options]"),
208     OPT_HELP(""),
209     OPT_HELP("Options:"),
210     OPT_HELP_OPTION,
211     OPT_CONF_OPTIONS,
212     OPT_END
213   }
214 };
215
216 int main(int argc UNUSED, char **argv)
217 {
218   cf_def_file = CONFIG_DIR "/subauthd";
219   cf_declare_section("SubauthD", &daemon_config, 0);
220   opt_parse(&options, argv+1);
221
222   main_init();
223   init_socket();
224
225   main_loop();
226   return 0;
227 }