OpenVAS Scanner 23.40.3
nasl_http2.c
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Greenbone AG
2 * SPDX-FileCopyrightText: 2002-2004 Tenable Network Security
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include "nasl_http2.h"
8
9#include "../misc/plugutils.h" /* plug_get_host_fqdn */
10#include "../misc/user_agent.h" /* for user_agent_get */
11#include "exec.h"
12#include "nasl_debug.h"
13#include "nasl_func.h"
14#include "nasl_global_ctxt.h"
15#include "nasl_lex_ctxt.h"
16#include "nasl_socket.h"
17#include "nasl_tree.h"
18#include "nasl_var.h"
19
20#include <ctype.h> /* for isspace */
21#include <curl/curl.h>
22#include <gnutls/gnutls.h>
23#include <gvm/base/prefs.h> /* for prefs_get */
24#include <gvm/util/kb.h> /* for kb_item_get_str */
25#include <string.h> /* for strlen */
26
27#undef G_LOG_DOMAIN
31#define G_LOG_DOMAIN "lib nasl"
32
33/*-----------------[ http2_* functions ]-------------------------------*/
34
45
49{
51 CURL *handle;
53};
54
55#define MAX_HANDLES 10
56
60
63static int
65{
66 static int last = 9000;
67 last++;
68
69 return last;
70}
71
85{
86 (void) lexic;
87 tree_cell *retc = NULL;
88 CURL *handle = curl_easy_init ();
89 unsigned int table_slot;
90
91 if (!handle)
92 return NULL;
93
94 for (table_slot = 0; table_slot < MAX_HANDLES; table_slot++)
95 if (!handle_table[table_slot] || !handle_table[table_slot]->handle_id)
96 break;
97
98 if (!(table_slot < MAX_HANDLES))
99 {
100 g_message ("%s: No space left in HTTP2 handle table", __func__);
101 curl_easy_cleanup (handle);
102 return NULL;
103 }
104
105 handle_table[table_slot] = g_malloc0 (sizeof (struct handle_table_s));
106 handle_table[table_slot]->handle = handle;
107 handle_table[table_slot]->handle_id = next_handle_id ();
108
110 retc->x.i_val = handle_table[table_slot]->handle_id;
111 return retc;
112}
113
128tree_cell *
130{
131 tree_cell *retc = NULL;
132 int handle_id = get_int_var_by_num (lexic, 0, -1);
133 unsigned int table_slot;
134 int ret = 0;
135
136 for (table_slot = 0; table_slot < MAX_HANDLES; table_slot++)
137 {
138 if (handle_table[table_slot]->handle_id == handle_id)
139 {
140 curl_easy_cleanup (handle_table[table_slot]->handle);
141 handle_table[table_slot]->handle = NULL;
142 handle_table[table_slot]->handle_id = 0;
143 handle_table[table_slot] = NULL;
144 }
145 else
146 {
147 g_message ("%s: Unknown handle identifier %d", __func__, handle_id);
148 ret = -1;
149 }
150 }
152 retc->x.i_val = ret;
153 return retc;
154}
155
158struct string
159{
160 char *ptr;
161 size_t len;
162};
163
168static void
169init_string (struct string *s)
170{
171 s->len = 0;
172 s->ptr = g_malloc0 (s->len + 1);
173 if (s->ptr == NULL)
174 {
175 g_warning ("%s: Error allocating memory for response", __func__);
176 return;
177 }
178 s->ptr[0] = '\0';
179}
180
186static size_t
187response_callback_fn (void *ptr, size_t size, size_t nmemb, void *struct_string)
188{
189 struct string *s = struct_string;
190 size_t new_len = s->len + size * nmemb;
191 char *ptr_aux = g_realloc (s->ptr, new_len + 1);
192 s->ptr = ptr_aux;
193 if (s->ptr == NULL)
194 {
195 g_warning ("%s: Error allocating memory for response", __func__);
196 return 0; // no memory left
197 }
198 memcpy (s->ptr + s->len, ptr, size * nmemb);
199 s->ptr[new_len] = '\0';
200 s->len = new_len;
201
202 return size * nmemb;
203}
204
210static size_t
211header_callback_fn (char *buffer, size_t size, size_t nmemb,
212 void *struct_string)
213{
214 struct string *s = struct_string;
215 size_t new_len = s->len + size * nmemb;
216 char *ptr_aux = g_realloc (s->ptr, new_len + 1);
217 s->ptr = ptr_aux;
218 if (s->ptr == NULL)
219 {
220 g_warning ("%s: Error allocating memory for response", __func__);
221 return 0; // no memory left
222 }
223 memcpy (s->ptr + s->len, buffer, size * nmemb);
224 s->ptr[new_len] = '\0';
225 s->len = new_len;
226
227 return size * nmemb;
228}
229
251static tree_cell *
252_http2_req (lex_ctxt *lexic, KEYWORD keyword)
253{
254 tree_cell *retc;
255 char *item = get_str_var_by_name (lexic, "item");
256 char *data = get_str_var_by_name (lexic, "data");
257 int port = get_int_var_by_name (lexic, "port", -1);
258 char *schema = get_str_var_by_name (lexic, "schema");
259 struct script_infos *script_infos = lexic->script_infos;
260 char *hostname;
261 GString *url = NULL;
262 CURL *handle = NULL;
263 int handle_id = get_int_var_by_name (lexic, "handle", -1);
264 struct string response, header_data;
265
266 if (item == NULL || port < 0 || handle_id < 0)
267 {
268 nasl_perror (lexic,
269 "Error : http2_* functions have the following syntax :\n");
270 nasl_perror (lexic, "http_*(handle: <handle>, port:<port>, item:<item> "
271 "[,schema:<schema>][, data:<data>]\n");
272 return NULL;
273 }
274
275 unsigned int table_slot;
276 for (table_slot = 0; table_slot < MAX_HANDLES; table_slot++)
277 {
278 if (handle_table[table_slot]->handle_id == handle_id)
279 break;
280 else
281 {
282 g_message ("%s: Unknown handle identifier %d", __func__, handle_id);
283 return NULL;
284 }
285 }
286
287 handle = handle_table[table_slot]->handle;
288
289 if (port <= 0 || port > 65535)
290 {
291 nasl_perror (lexic, "http2_req: invalid value %d for port parameter\n",
292 port);
293 return NULL;
294 }
295
296 // Fork here for every vhost
298 if (hostname == NULL)
299 return NULL;
300
301 curl_easy_reset (handle);
302
303 // force http2
304 curl_easy_setopt (handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
305
306 // Build URL
307 url = schema ? g_string_new (schema) : g_string_new ("https");
308 g_string_append (url, "://");
309 g_string_append (url, hostname);
310
311 /* Servers should not have a problem with port 80 or 443 appended.
312 * RFC2616 allows to omit the port in which case the default port for
313 * that service is assumed.
314 * However, some servers like IIS/OWA wrongly respond with a "404"
315 * instead of a "200" in case the port is appended. Because of this,
316 * ports 80 and 443 are not appended.
317 */
318 if (port != 80 && port != 443)
319 {
320 char buf[12];
321 snprintf (buf, sizeof (buf), ":%d", port);
322 g_string_append (url, buf);
323 }
324 g_string_append (url, item);
325
326 g_message ("%s: URL: %s", __func__, url->str);
327 // Set URL
328 if (curl_easy_setopt (handle, CURLOPT_URL, url->str) != CURLE_OK)
329 {
330 g_warning ("Not possible to set the URL");
331 curl_easy_cleanup (handle);
332 return NULL;
333 }
334 g_string_free (url, TRUE);
335
336 // Accept an insecure connection. Don't verify the server certificate
337 curl_easy_setopt (handle, CURLOPT_SSL_VERIFYPEER, 0L);
338 curl_easy_setopt (handle, CURLOPT_SSL_VERIFYHOST, 0L);
339
340 // Set User Agent
341 char *ua = NULL;
342 if ((user_agent_get (lexic->script_infos->ipc_context, &ua) == -2)
344 {
345 g_message ("Not possible to send the User Agent to the host process. "
346 "Invalid IPC context");
347 }
348 if (ua)
349 {
350 curl_easy_setopt (handle, CURLOPT_USERAGENT, g_strdup (url->str));
351 g_free (ua);
352 }
353
354 // Init the struct where the response is stored and set the callback function
355 init_string (&response);
356 curl_easy_setopt (handle, CURLOPT_WRITEFUNCTION, response_callback_fn);
357 curl_easy_setopt (handle, CURLOPT_WRITEDATA, &response);
358
359 init_string (&header_data);
360 curl_easy_setopt (handle, CURLOPT_HEADERFUNCTION, header_callback_fn);
361 curl_easy_setopt (handle, CURLOPT_HEADERDATA, &header_data);
362
363 switch (keyword)
364 {
365 case DELETE:
366 curl_easy_setopt (handle, CURLOPT_CUSTOMREQUEST, "DELETE");
367 break;
368 case HEAD:
369 curl_easy_setopt (handle, CURLOPT_NOBODY, 1L);
370 break;
371 case PUT:
372 curl_easy_setopt (handle, CURLOPT_CUSTOMREQUEST, "PUT");
373 if (data)
374 {
375 curl_easy_setopt (handle, CURLOPT_POSTFIELDS, data);
376 curl_easy_setopt (handle, CURLOPT_POSTFIELDSIZE, strlen (data));
377 }
378 break;
379 case GET:
380 curl_easy_setopt (handle, CURLOPT_HTTPGET, 1L);
381 break;
382 case POST:
383 // Set body. POST is set automatically with this options
384 if (data)
385 {
386 curl_easy_setopt (handle, CURLOPT_POSTFIELDS, data);
387 curl_easy_setopt (handle, CURLOPT_POSTFIELDSIZE, strlen (data));
388 }
389 break;
390 default:
391 g_message ("%s: Invalid http method.", __func__);
392 break;
393 }
394
395 int ret = CURLE_OK;
396 if ((ret = curl_easy_perform (handle)) != CURLE_OK)
397 {
398 g_warning ("%s: Error sending request: %d", __func__, ret);
399 curl_easy_cleanup (handle);
400 g_free (response.ptr);
401 return NULL;
402 }
403
404 GString *complete_resp = g_string_new (header_data.ptr);
405 g_string_append (complete_resp, "\n");
406 g_string_append (complete_resp, response.ptr);
407 g_free (response.ptr);
408 g_free (header_data.ptr);
409
410 long http_code = -1;
411 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &http_code);
412 handle_table[table_slot]->http_code = http_code;
413
415 retc->size = complete_resp->len;
416 retc->x.str_val = g_strdup (complete_resp->str);
417
418 g_string_free (complete_resp, TRUE);
419 return retc;
420}
421
435tree_cell *
437{
438 tree_cell *retc = NULL;
439 unsigned int table_slot;
440 int handle_id = get_int_var_by_name (lexic, "handle", -1);
441
442 if (handle_id < 0)
443 {
444 nasl_perror (lexic,
445 "Error : http2_* functions have the following syntax :\n");
446 nasl_perror (lexic, "http_*(handle: <handle>\n");
447 return NULL;
448 }
449
450 for (table_slot = 0; table_slot < MAX_HANDLES; table_slot++)
451 {
452 if (handle_table[table_slot]->handle_id == handle_id)
453 break;
454 else
455 {
456 g_message ("%s: Unknown handle identifier %d", __func__, handle_id);
457 return NULL;
458 }
459 }
460
462 retc->x.i_val = handle_table[table_slot]->http_code;
463 return retc;
464}
465
481tree_cell *
483{
484 tree_cell *retc = NULL;
485 struct curl_slist *customheader = NULL;
486 unsigned int table_slot;
487 CURL *handle;
488 int handle_id = get_int_var_by_name (lexic, "handle", -1);
489 char *headeritem = get_str_var_by_name (lexic, "header_item");
490
491 if (handle_id < 0 || headeritem == NULL)
492 {
493 nasl_perror (lexic,
494 "Error : http2_* functions have the following syntax :\n");
495 nasl_perror (lexic,
496 "http_*(handle: <handle>, header_item:<header_item>\n");
497 return NULL;
498 }
499
500 for (table_slot = 0; table_slot < MAX_HANDLES; table_slot++)
501 {
502 if (handle_table[table_slot]->handle_id == handle_id)
503 break;
504 else
505 {
506 g_message ("%s: Unknown handle identifier %d", __func__, handle_id);
507 return NULL;
508 }
509 }
510 handle = handle_table[table_slot]->handle;
511
512 // SET Content type
513 customheader = curl_slist_append (customheader, headeritem);
514 curl_easy_setopt (handle, CURLOPT_HTTPHEADER, customheader);
515
517 retc->x.i_val = 0;
518
519 return retc;
520}
521
524tree_cell *
526{
527 return _http2_req (lexic, GET);
528}
529
532tree_cell *
534{
535 return _http2_req (lexic, HEAD);
536}
537
540tree_cell *
542{
543 return _http2_req (lexic, POST);
544}
545
548tree_cell *
550{
551 return _http2_req (lexic, DELETE);
552}
553
556tree_cell *
558{
559 return _http2_req (lexic, PUT);
560}
void nasl_perror(lex_ctxt *lexic, char *msg,...)
Definition nasl_debug.c:105
enum KEYWORD_E KEYWORD
Allowed methods.
KEYWORD_E
Allowed methods.
Definition nasl_http2.c:38
@ HEAD
Definition nasl_http2.c:43
@ GET
Definition nasl_http2.c:40
@ POST
Definition nasl_http2.c:39
@ DELETE
Definition nasl_http2.c:42
@ PUT
Definition nasl_http2.c:41
tree_cell * nasl_http2_handle(lex_ctxt *lexic)
Creates a handle for http requests.
Definition nasl_http2.c:84
#define MAX_HANDLES
Definition nasl_http2.c:55
static struct handle_table_s * handle_table[MAX_HANDLES]
Handle Table.
Definition nasl_http2.c:59
tree_cell * nasl_http2_get_response_code(lex_ctxt *lexic)
Get the http response code after performing a HTTP request.
Definition nasl_http2.c:436
tree_cell * nasl_http2_head(lex_ctxt *lexic)
Wrapper function for HEAD request. See _http2_req.
Definition nasl_http2.c:533
tree_cell * nasl_http2_get(lex_ctxt *lexic)
Wrapper function for GET request. See _http2_req.
Definition nasl_http2.c:525
tree_cell * nasl_http2_close_handle(lex_ctxt *lexic)
Close a handle for http requests previously initialized.
Definition nasl_http2.c:129
tree_cell * nasl_http2_put(lex_ctxt *lexic)
Wrapper function for PUT request. See _http2_req.
Definition nasl_http2.c:557
tree_cell * nasl_http2_set_custom_header(lex_ctxt *lexic)
Set a custom header element in the header.
Definition nasl_http2.c:482
tree_cell * nasl_http2_delete(lex_ctxt *lexic)
Wrapper function for DELETE request. See _http2_req.
Definition nasl_http2.c:549
static size_t response_callback_fn(void *ptr, size_t size, size_t nmemb, void *struct_string)
Call back function to stored the response.
Definition nasl_http2.c:187
static size_t header_callback_fn(char *buffer, size_t size, size_t nmemb, void *struct_string)
Call back function to stored the header.
Definition nasl_http2.c:211
static int next_handle_id(void)
Get the new available handle identifier.
Definition nasl_http2.c:64
static void init_string(struct string *s)
Initialize the string struct to hold the response or header.
Definition nasl_http2.c:169
static tree_cell * _http2_req(lex_ctxt *lexic, KEYWORD keyword)
Perform an HTTP request. Forcing HTTP2 if possible.
Definition nasl_http2.c:252
tree_cell * nasl_http2_post(lex_ctxt *lexic)
Wrapper function for POST request. See _http2_req.
Definition nasl_http2.c:541
struct struct_lex_ctxt lex_ctxt
char * get_str_var_by_name(lex_ctxt *, const char *)
Definition nasl_var.c:1118
long int get_int_var_by_num(lex_ctxt *, int, int)
Definition nasl_var.c:1094
long int get_int_var_by_name(lex_ctxt *, const char *, int)
Definition nasl_var.c:1101
tree_cell * alloc_typed_cell(int typ)
Definition nasl_tree.c:25
@ CONST_DATA
Definition nasl_tree.h:82
@ CONST_INT
Definition nasl_tree.h:79
struct TC tree_cell
const char * hostname
char * plug_get_host_fqdn(struct script_infos *args)
Definition plugutils.c:291
Header file for module plugutils.
long int i_val
Definition nasl_tree.h:104
long int size
Definition nasl_tree.h:99
union TC::@332262321161220155002104006201360276211317150140 x
char * str_val
Definition nasl_tree.h:103
Struct to store handles.
Definition nasl_http2.c:49
struct ipc_context * ipc_context
Definition scanneraux.h:31
Define a string struct for storing the response.
struct script_infos * script_infos
static size_t response_callback_fn(void *ptr, size_t size, size_t nmemb, void *struct_string)
Call back function to stored the response.
int user_agent_get(struct ipc_context *ipc_context, char **useragent)
Get user-agent.
Definition user_agent.c:114
Header file: user agent functions prototypes.