source: ogServer-Git/src/core.c @ eaf7ed9

Last change on this file since eaf7ed9 was 4e2ef1a, checked in by OpenGnSys Support Team <soporte-og@…>, 3 years ago

#1067 fix use-after-free in deliver pending command

Do not release the json object twice, once from og_send_request() and
again og_cmd_free().

Valgrind reports:

==11885== Invalid read of size 8
==11885== at 0x117B9A: json_decref (jansson.h:128)
==11885== by 0x117B9A: og_cmd_free (rest.c:2409)
==11885== by 0x113465: og_agent_deliver_pending_cmd (core.c:211)
==11885== by 0x113465: og_agent_read_cb (core.c:256)
==11885== by 0x4E41D72: ev_invoke_pending (in /usr/lib/x86_64-linux-gnu/libev.so.4.0.0)
==11885== by 0x4E453DD: ev_run (in /usr/lib/x86_64-linux-gnu/libev.so.4.0.0)
==11885== by 0x110C2D: ev_loop (ev.h:835)
==11885== by 0x110C2D: main (main.c:104)
==11885== Address 0x8e7e988 is 8 bytes inside a block of size 72 free'd
==11885== at 0x4C32D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11885== by 0x117437: json_decref (jansson.h:129)
==11885== by 0x117437: og_send_request (rest.c:330)
==11885== by 0x113454: og_agent_deliver_pending_cmd (core.c:208)
==11885== by 0x113454: og_agent_read_cb (core.c:256)
==11885== by 0x4E41D72: ev_invoke_pending (in /usr/lib/x86_64-linux-gnu/libev.so.4.0.0)
==11885== by 0x4E453DD: ev_run (in /usr/lib/x86_64-linux-gnu/libev.so.4.0.0)
==11885== by 0x110C2D: ev_loop (ev.h:835)
==11885== by 0x110C2D: main (main.c:104)
==11885== Block was alloc'd at
==11885== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11885== by 0x526461A: json_object (in /usr/lib/x86_64-linux-gnu/libjansson.so.4.11.0)
==11885== by 0x116A07: og_cmd_legacy_image_restore (rest.c:2627)
==11885== by 0x116A07: og_cmd_legacy (rest.c:2757)
==11885== by 0x116A07: og_queue_task_command (rest.c:2848)
==11885== by 0x118284: og_dbi_queue_command (rest.c:3109)
==11885== by 0x118284: og_schedule_run (rest.c:3190)
==11885== by 0x1147B9: og_agent_timer_cb (schedule.c:445)
==11885== by 0x4E41D72: ev_invoke_pending (in /usr/lib/x86_64-linux-gnu/libev.so.4.0.0)
==11885== by 0x4E453DD: ev_run (in /usr/lib/x86_64-linux-gnu/libev.so.4.0.0)
==11885== by 0x110C2D: ev_loop (ev.h:835)
==11885== by 0x110C2D: main (main.c:104)

  • Property mode set to 100644
File size: 9.2 KB
Line 
1/*
2 * Copyright (C) 2020-2021 Soleta Networks <info@soleta.eu>
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Affero General Public License as published by the
6 * Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 */
9
10#include "ogAdmServer.h"
11#include "dbi.h"
12#include "utils.h"
13#include "list.h"
14#include "rest.h"
15#include "wol.h"
16#include "client.h"
17#include "json.h"
18#include "schedule.h"
19#include <syslog.h>
20#include <sys/ioctl.h>
21#include <ifaddrs.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <netinet/tcp.h>
25#include <fcntl.h>
26#include <jansson.h>
27#include <time.h>
28
29static void og_client_release(struct ev_loop *loop, struct og_client *cli)
30{
31        list_del(&cli->list);
32        ev_timer_stop(loop, &cli->timer);
33        ev_io_stop(loop, &cli->io);
34        close(cli->io.fd);
35        free(cli);
36}
37
38static int og_client_payload_too_large(struct og_client *cli)
39{
40        char buf[] = "HTTP/1.1 413 Payload Too Large\r\n"
41                     "Content-Length: 0\r\n\r\n";
42
43        send(og_client_socket(cli), buf, strlen(buf), 0);
44
45        return -1;
46}
47
48static int og_client_state_recv_hdr_rest(struct og_client *cli)
49{
50        char *ptr;
51
52        ptr = strstr(cli->buf, "\r\n\r\n");
53        if (!ptr)
54                return 0;
55
56        cli->msg_len = ptr - cli->buf + 4;
57
58        ptr = strstr(cli->buf, "Content-Length: ");
59        if (ptr) {
60                sscanf(ptr, "Content-Length: %i[^\r\n]", &cli->content_length);
61                if (cli->content_length < 0)
62                        return -1;
63                cli->msg_len += cli->content_length;
64        }
65
66        ptr = strstr(cli->buf, "Authorization: ");
67        if (ptr)
68                sscanf(ptr, "Authorization: %63[^\r\n]", cli->auth_token);
69
70        return 1;
71}
72
73static int og_client_recv(struct og_client *cli, int events)
74{
75        struct ev_io *io = &cli->io;
76        int ret;
77
78        if (events & EV_ERROR) {
79                syslog(LOG_ERR, "unexpected error event from client %s:%hu\n",
80                               inet_ntoa(cli->addr.sin_addr),
81                               ntohs(cli->addr.sin_port));
82                return 0;
83        }
84
85        ret = recv(io->fd, cli->buf + cli->buf_len,
86                   sizeof(cli->buf) - cli->buf_len, 0);
87        if (ret <= 0) {
88                if (ret < 0) {
89                        syslog(LOG_ERR, "error reading from client %s:%hu (%s)\n",
90                               inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port),
91                               strerror(errno));
92                }
93                return ret;
94        }
95
96        return ret;
97}
98
99static void og_client_read_cb(struct ev_loop *loop, struct ev_io *io, int events)
100{
101        struct og_client *cli;
102        int ret;
103
104        cli = container_of(io, struct og_client, io);
105
106        ret = og_client_recv(cli, events);
107        if (ret <= 0)
108                goto close;
109
110        ev_timer_again(loop, &cli->timer);
111
112        cli->buf_len += ret;
113        if (cli->buf_len >= sizeof(cli->buf)) {
114                syslog(LOG_ERR, "client request from %s:%hu is too long\n",
115                       inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port));
116                og_client_payload_too_large(cli);
117                goto close;
118        }
119
120        switch (cli->state) {
121        case OG_CLIENT_RECEIVING_HEADER:
122                ret = og_client_state_recv_hdr_rest(cli);
123                if (ret < 0)
124                        goto close;
125                if (!ret)
126                        return;
127
128                cli->state = OG_CLIENT_RECEIVING_PAYLOAD;
129                /* Fall through. */
130        case OG_CLIENT_RECEIVING_PAYLOAD:
131                /* Still not enough data to process request. */
132                if (cli->buf_len < cli->msg_len)
133                        return;
134
135                cli->state = OG_CLIENT_PROCESSING_REQUEST;
136                /* fall through. */
137        case OG_CLIENT_PROCESSING_REQUEST:
138                ret = og_client_state_process_payload_rest(cli);
139                if (ret < 0) {
140                        syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n",
141                               inet_ntoa(cli->addr.sin_addr),
142                               ntohs(cli->addr.sin_port));
143                }
144                goto close;
145        default:
146                syslog(LOG_ERR, "unknown state, critical internal error\n");
147                goto close;
148        }
149        return;
150close:
151        og_client_release(loop, cli);
152}
153
154enum og_agent_state {
155        OG_AGENT_RECEIVING_HEADER       = 0,
156        OG_AGENT_RECEIVING_PAYLOAD,
157        OG_AGENT_PROCESSING_RESPONSE,
158};
159
160static int og_agent_state_recv_hdr_rest(struct og_client *cli)
161{
162        char *ptr;
163
164        ptr = strstr(cli->buf, "\r\n\r\n");
165        if (!ptr)
166                return 0;
167
168        cli->msg_len = ptr - cli->buf + 4;
169
170        ptr = strstr(cli->buf, "Content-Length: ");
171        if (ptr) {
172                sscanf(ptr, "Content-Length: %i[^\r\n]", &cli->content_length);
173                if (cli->content_length < 0)
174                        return -1;
175                cli->msg_len += cli->content_length;
176        }
177
178        return 1;
179}
180
181static void og_agent_reset_state(struct og_client *cli)
182{
183        cli->state = OG_AGENT_RECEIVING_HEADER;
184        cli->buf_len = 0;
185        cli->content_length = 0;
186        memset(cli->buf, 0, sizeof(cli->buf));
187}
188
189#define OG_AGENT_CMD_TIMEOUT 900
190
191static void og_agent_deliver_pending_cmd(struct og_client *cli)
192{
193        struct timeval now, elapsed;
194        const struct og_cmd *cmd;
195
196        cmd = og_cmd_find(inet_ntoa(cli->addr.sin_addr));
197        if (!cmd)
198                return;
199
200        gettimeofday(&now, NULL);
201        timersub(&now, &cmd->tv, &elapsed);
202        if (elapsed.tv_sec >= OG_AGENT_CMD_TIMEOUT) {
203                og_dbi_update_action(cmd->id, false);
204                og_cmd_free(cmd);
205                return;
206        }
207
208        json_incref(cmd->json);
209        og_send_request(cmd->method, cmd->type, &cmd->params, cmd->json);
210        cli->last_cmd_id = cmd->id;
211
212        og_cmd_free(cmd);
213}
214
215static void og_agent_read_cb(struct ev_loop *loop, struct ev_io *io, int events)
216{
217        struct og_client *cli;
218        int ret;
219
220        cli = container_of(io, struct og_client, io);
221
222        ret = og_client_recv(cli, events);
223        if (ret <= 0)
224                goto close;
225
226        ev_timer_again(loop, &cli->timer);
227
228        cli->buf_len += ret;
229        if (cli->buf_len >= sizeof(cli->buf)) {
230                syslog(LOG_ERR, "client request from %s:%hu is too long\n",
231                       inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port));
232                goto close;
233        }
234
235        switch (cli->state) {
236        case OG_AGENT_RECEIVING_HEADER:
237                ret = og_agent_state_recv_hdr_rest(cli);
238                if (ret < 0)
239                        goto close;
240                if (!ret)
241                        return;
242
243                cli->state = OG_AGENT_RECEIVING_PAYLOAD;
244                /* Fall through. */
245        case OG_AGENT_RECEIVING_PAYLOAD:
246                /* Still not enough data to process request. */
247                if (cli->buf_len < cli->msg_len)
248                        return;
249
250                cli->state = OG_AGENT_PROCESSING_RESPONSE;
251                /* fall through. */
252        case OG_AGENT_PROCESSING_RESPONSE:
253                ret = og_agent_state_process_response(cli);
254                if (ret < 0) {
255                        goto close;
256                } else if (ret == 0) {
257                        og_agent_deliver_pending_cmd(cli);
258                }
259
260                og_agent_reset_state(cli);
261                break;
262        default:
263                syslog(LOG_ERR, "unknown state, critical internal error\n");
264                goto close;
265        }
266        return;
267close:
268        og_client_release(loop, cli);
269}
270
271static void og_client_timer_cb(struct ev_loop *loop, ev_timer *timer, int events)
272{
273        struct og_client *cli;
274
275        cli = container_of(timer, struct og_client, timer);
276        if (cli->agent) {
277                ev_timer_again(loop, &cli->timer);
278                return;
279        }
280        syslog(LOG_ERR, "timeout request for client %s:%hu\n",
281               inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port));
282
283        og_client_release(loop, cli);
284}
285
286static void og_agent_send_refresh(struct og_client *cli)
287{
288        struct og_msg_params params;
289        int err;
290
291        params.ips_array[0] = inet_ntoa(cli->addr.sin_addr);
292        params.ips_array_len = 1;
293
294        err = og_send_request(OG_METHOD_GET, OG_CMD_REFRESH, &params, NULL);
295        if (err < 0) {
296                syslog(LOG_ERR, "Can't send refresh to: %s\n",
297                       params.ips_array[0]);
298        } else {
299                syslog(LOG_INFO, "Sent refresh to: %s\n",
300                       params.ips_array[0]);
301        }
302}
303
304/* Shut down connection if there is no complete message after 10 seconds. */
305#define OG_CLIENT_TIMEOUT       10.
306
307/* Agent client operation might take longer, shut down after 30 seconds. */
308#define OG_AGENT_CLIENT_TIMEOUT 30.
309
310#define OG_TCP_KEEPALIVE_IDLE   60
311#define OG_TCP_KEEPALIVE_INTL   30
312#define OG_TCP_KEEPALIVE_CNT    4
313
314int socket_rest, socket_agent_rest;
315
316void og_server_accept_cb(struct ev_loop *loop, struct ev_io *io, int events)
317{
318        int intl = OG_TCP_KEEPALIVE_INTL, cnt = OG_TCP_KEEPALIVE_CNT;
319        int on = 1, idle = OG_TCP_KEEPALIVE_IDLE;
320        struct sockaddr_in client_addr;
321        socklen_t addrlen = sizeof(client_addr);
322        struct og_client_wol *cli_wol;
323        struct og_client *cli;
324        int client_sd;
325
326        if (events & EV_ERROR)
327                return;
328
329        client_sd = accept(io->fd, (struct sockaddr *)&client_addr, &addrlen);
330        if (client_sd < 0) {
331                syslog(LOG_ERR, "cannot accept client connection\n");
332                return;
333        }
334
335        setsockopt(client_sd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(int));
336        setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(int));
337        setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPINTVL, &intl, sizeof(int));
338        setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(int));
339
340        cli_wol = og_client_wol_find(&client_addr.sin_addr);
341        if (cli_wol)
342                og_client_wol_destroy(cli_wol);
343
344        cli = (struct og_client *)calloc(1, sizeof(struct og_client));
345        if (!cli) {
346                close(client_sd);
347                return;
348        }
349        memcpy(&cli->addr, &client_addr, sizeof(client_addr));
350
351        if (io->fd == socket_agent_rest) {
352                cli->agent = true;
353                ev_io_init(&cli->io, og_agent_read_cb, client_sd, EV_READ);
354        } else {
355                ev_io_init(&cli->io, og_client_read_cb, client_sd, EV_READ);
356        }
357
358        ev_io_start(loop, &cli->io);
359        ev_init(&cli->timer, og_client_timer_cb);
360        if (io->fd == socket_agent_rest)
361                cli->timer.repeat = OG_AGENT_CLIENT_TIMEOUT;
362        else
363                cli->timer.repeat = OG_CLIENT_TIMEOUT;
364
365        ev_timer_again(loop, &cli->timer);
366        og_client_add(cli);
367
368        if (io->fd == socket_agent_rest) {
369                og_agent_send_refresh(cli);
370        }
371}
372
373int og_socket_server_init(const char *port)
374{
375        struct sockaddr_in local;
376        int sd, on = 1;
377
378        sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
379        if (sd < 0) {
380                syslog(LOG_ERR, "cannot create main socket\n");
381                return -1;
382        }
383        setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int));
384
385        local.sin_addr.s_addr = htonl(INADDR_ANY);
386        local.sin_family = AF_INET;
387        local.sin_port = htons(atoi(port));
388
389        if (bind(sd, (struct sockaddr *) &local, sizeof(local)) < 0) {
390                close(sd);
391                syslog(LOG_ERR, "cannot bind socket\n");
392                return -1;
393        }
394
395        listen(sd, 250);
396
397        return sd;
398}
Note: See TracBrowser for help on using the repository browser.