1 /* Copyright (C) 2014-2021 Greenbone Networks GmbH
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /**
21 * @file
22 * @brief API for Open Scanner Protocol communication.
23 */
24
25 #include "osp.h"
26
27 #include "../base/hosts.h" /* for gvm_get_host_type */
28 #include "../util/serverutils.h" /* for gvm_server_close, gvm_server_open_w... */
29
30 #include <assert.h> /* for assert */
31 #include <gnutls/gnutls.h> /* for gnutls_session_int, gnutls_session_t */
32 #include <stdarg.h> /* for va_list */
33 #include <stdio.h> /* for FILE, fprintf and related functions */
34 #include <stdlib.h> /* for NULL, atoi */
35 #include <string.h> /* for strcmp, strlen, strncpy */
36 #include <sys/socket.h> /* for AF_UNIX, connect, socket, SOCK_STREAM */
37 #include <sys/un.h> /* for sockaddr_un, sa_family_t */
38 #include <unistd.h> /* for close */
39
40 #undef G_LOG_DOMAIN
41 /**
42 * @brief GLib log domain.
43 */
44 #define G_LOG_DOMAIN "libgvm osp"
45
46 /**
47 * @brief Struct holding options for OSP connection.
48 */
49 struct osp_connection
50 {
51 gnutls_session_t session; /**< Pointer to GNUTLS Session. */
52 int socket; /**< Socket. */
53 char *host; /**< Host. */
54 int port; /**< Port. */
55 };
56
57 /**
58 * @brief Struct holding options for OSP parameters.
59 */
60 struct osp_param
61 {
62 char *id; /**< Parameter id. */
63 char *name; /**< Parameter name. */
64 char *desc; /**< Parameter description. */
65 char *def; /**< Default value. */
66 osp_param_type_t type; /**< Parameter type. */
67 int mandatory; /**< If mandatory or not. */
68 };
69
70 /**
71 * @brief Struct credential information for OSP.
72 */
73 struct osp_credential
74 {
75 gchar *type; /**< Credential type */
76 gchar *service; /**< Service the credential is for */
77 gchar *port; /**< Port the credential is for */
78 GHashTable *auth_data; /**< Authentication data (username, password, etc.)*/
79 };
80
81 /**
82 * @brief Struct holding target information.
83 */
84 struct osp_target
85 {
86 GSList *credentials; /** Credentials to use in the scan */
87 gchar *exclude_hosts; /** String defining one or many hosts to exclude */
88 gchar *hosts; /** String defining one or many hosts to scan */
89 gchar *ports; /** String defining the ports to scan */
90 gchar *finished_hosts; /** String defining hosts to exclude as finished */
91 /* Alive test methods can be specified either as bitfield or via selection of
92 individual methods */
93 int alive_test; /** Value defining an alive test method */
94 gboolean icmp;
95 gboolean tcp_syn;
96 gboolean tcp_ack;
97 gboolean arp;
98 gboolean consider_alive;
99 int reverse_lookup_unify; /** Value defining reverse_lookup_unify opt */
100 int reverse_lookup_only; /** Value defining reverse_lookup_only opt */
101 };
102
103 /**
104 * @brief Struct holding vt_group information
105 */
106 struct osp_vt_group
107 {
108 gchar *filter;
109 };
110
111 /**
112 * @brief Struct holding vt_group information
113 */
114 struct osp_vt_single
115 {
116 gchar *vt_id;
117 GHashTable *vt_values;
118 };
119
120 static int
121 osp_send_command (osp_connection_t *, entity_t *, const char *, ...)
122 __attribute__ ((__format__ (__printf__, 3, 4)));
123
124 /**
125 * @brief Open a new connection to an OSP server.
126 *
127 * @param[in] host Host of OSP server.
128 * @param[in] port Port of OSP server.
129 * @param[in] cacert CA public key.
130 * @param[in] cert Client public key.
131 * @param[in] key Client private key.
132 *
133 * @return New osp connection, NULL if error.
134 */
135 osp_connection_t *
osp_connection_new(const char * host,int port,const char * cacert,const char * cert,const char * key)136 osp_connection_new (const char *host, int port, const char *cacert,
137 const char *cert, const char *key)
138 {
139 osp_connection_t *connection;
140
141 if (host && *host == '/')
142 {
143 struct sockaddr_un addr;
144
145 connection = g_malloc0 (sizeof (*connection));
146 connection->socket = socket (AF_UNIX, SOCK_STREAM, 0);
147 if (connection->socket == -1)
148 {
149 g_free (connection);
150 return NULL;
151 }
152
153 addr.sun_family = AF_UNIX;
154 strcpy (addr.sun_path, host);
155 if (connect (connection->socket, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1)
156 {
157 close (connection->socket);
158 g_free (connection);
159 return NULL;
160 }
161 }
162 else
163 {
164 if (port <= 0 || port > 65535)
165 return NULL;
166 if (!host || gvm_get_host_type (host) == -1)
167 return NULL;
168 if (!cert || !key || !cacert)
169 return NULL;
170
171 connection = g_malloc0 (sizeof (*connection));
172 connection->socket = gvm_server_open_with_cert (
173 &connection->session, host, port, cacert, cert, key);
174 }
175 if (connection->socket == -1)
176 {
177 g_free (connection);
178 return NULL;
179 }
180
181 connection->host = g_strdup (host);
182 connection->port = port;
183 return connection;
184 }
185
186 /**
187 * @brief Send a command to an OSP server.
188 *
189 * @param[in] connection Connection to OSP server.
190 * @param[out] response Response from OSP server.
191 * @param[in] fmt OSP Command to send.
192 *
193 * @return 0 and response, 1 if error.
194 */
195 int
osp_send_command(osp_connection_t * connection,entity_t * response,const char * fmt,...)196 osp_send_command (osp_connection_t *connection, entity_t *response,
197 const char *fmt, ...)
198 {
199 va_list ap;
200 int rc = 1;
201
202 va_start (ap, fmt);
203
204 if (!connection || !fmt || !response)
205 goto out;
206
207 if (*connection->host == '/')
208 {
209 if (gvm_socket_vsendf (connection->socket, fmt, ap) == -1)
210 goto out;
211 if (read_entity_s (connection->socket, response))
212 goto out;
213 }
214 else
215 {
216 if (gvm_server_vsendf (&connection->session, fmt, ap) == -1)
217 goto out;
218 if (read_entity (&connection->session, response))
219 goto out;
220 }
221
222 rc = 0;
223
224 out:
225 va_end (ap);
226
227 return rc;
228 }
229
230 /**
231 * @brief Close a connection to an OSP server.
232 *
233 * @param[in] connection Connection to OSP server to close.
234 */
235 void
osp_connection_close(osp_connection_t * connection)236 osp_connection_close (osp_connection_t *connection)
237 {
238 if (!connection)
239 return;
240
241 if (*connection->host == '/')
242 close (connection->socket);
243 else
244 gvm_server_close (connection->socket, connection->session);
245 g_free (connection->host);
246 g_free (connection);
247 }
248
249 /**
250 * @brief Get the scanner version from an OSP server.
251 *
252 * @param[in] connection Connection to an OSP server.
253 * @param[out] s_name Parsed scanner name.
254 * @param[out] s_version Parsed scanner version.
255 * @param[out] d_name Parsed scanner name.
256 * @param[out] d_version Parsed scanner version.
257 * @param[out] p_name Parsed scanner name.
258 * @param[out] p_version Parsed scanner version.
259 *
260 * @return 0 if success, 1 if error.
261 */
262 int
osp_get_version(osp_connection_t * connection,char ** s_name,char ** s_version,char ** d_name,char ** d_version,char ** p_name,char ** p_version)263 osp_get_version (osp_connection_t *connection, char **s_name, char **s_version,
264 char **d_name, char **d_version, char **p_name,
265 char **p_version)
266 {
267 entity_t entity, child, gchild;
268
269 if (!connection)
270 return 1;
271
272 if (osp_send_command (connection, &entity, "<get_version/>"))
273 return 1;
274
275 child = entity_child (entity, "scanner");
276 if (!child)
277 goto err_get_version;
278 gchild = entity_child (child, "name");
279 if (!gchild)
280 goto err_get_version;
281 if (s_name)
282 *s_name = g_strdup (entity_text (gchild));
283 gchild = entity_child (child, "version");
284 if (!gchild)
285 goto err_get_version;
286 if (s_version)
287 *s_version = g_strdup (entity_text (gchild));
288
289 child = entity_child (entity, "daemon");
290 if (!child)
291 goto err_get_version;
292 gchild = entity_child (child, "name");
293 if (!gchild)
294 goto err_get_version;
295 if (d_name)
296 *d_name = g_strdup (entity_text (gchild));
297 gchild = entity_child (child, "version");
298 if (!gchild)
299 goto err_get_version;
300 if (d_version)
301 *d_version = g_strdup (entity_text (gchild));
302
303 child = entity_child (entity, "protocol");
304 if (!child)
305 goto err_get_version;
306 gchild = entity_child (child, "name");
307 if (!gchild)
308 goto err_get_version;
309 if (p_name)
310 *p_name = g_strdup (entity_text (gchild));
311 gchild = entity_child (child, "version");
312 if (!gchild)
313 goto err_get_version;
314 if (p_version)
315 *p_version = g_strdup (entity_text (gchild));
316
317 free_entity (entity);
318 return 0;
319
320 err_get_version:
321 g_warning ("Erroneous OSP <get_version/> response.");
322 if (s_name)
323 g_free (*s_name);
324 if (s_version)
325 g_free (*s_version);
326 if (d_name)
327 g_free (*d_name);
328 if (d_version)
329 g_free (*d_version);
330 if (p_name)
331 g_free (*p_name);
332 if (p_version)
333 g_free (*p_version);
334 free_entity (entity);
335 return 1;
336 }
337
338 /**
339 * @brief Get the VTs version from an OSP server.
340 *
341 * @param[in] connection Connection to an OSP server.
342 * @param[out] vts_version Parsed scanner version.
343 * @param[out] error Pointer to error, if any.
344 *
345 * @return 0 if success, 1 if error.
346 */
347 int
osp_get_vts_version(osp_connection_t * connection,char ** vts_version,char ** error)348 osp_get_vts_version (osp_connection_t *connection, char **vts_version,
349 char **error)
350 {
351 entity_t entity, vts;
352 const char *version;
353 const char *status, *status_text;
354 osp_get_vts_opts_t get_vts_opts;
355
356 if (!connection)
357 return 1;
358
359 get_vts_opts = osp_get_vts_opts_default;
360 get_vts_opts.version_only = 1;
361 if (osp_get_vts_ext (connection, get_vts_opts, &entity))
362 return 1;
363
364 status = entity_attribute (entity, "status");
365
366 if (status != NULL && !strcmp (status, "400"))
367 {
368 status_text = entity_attribute (entity, "status_text");
369 g_debug ("%s: %s - %s.", __func__, status, status_text);
370 if (error)
371 *error = g_strdup (status_text);
372 free_entity (entity);
373 return 1;
374 }
375
376 vts = entity_child (entity, "vts");
377 if (!vts)
378 {
379 g_warning ("%s: element VTS missing.", __func__);
380 free_entity (entity);
381 return 1;
382 }
383
384 version = entity_attribute (vts, "vts_version");
385
386 if (vts_version)
387 *vts_version = g_strdup (version);
388
389 free_entity (entity);
390 return 0;
391 }
392
393 /**
394 * @brief Get all VTs from an OSP server.
395 *
396 * @param[in] connection Connection to an OSP server.
397 * @param[out] vts VTs.
398 *
399 * @return 0 if success, 1 if error.
400 */
401 int
osp_get_vts(osp_connection_t * connection,entity_t * vts)402 osp_get_vts (osp_connection_t *connection, entity_t *vts)
403 {
404 if (!connection)
405 return 1;
406
407 if (vts == NULL)
408 return 1;
409
410 if (osp_send_command (connection, vts, "<get_vts/>"))
411 return 1;
412
413 return 0;
414 }
415
416 /**
417 * @brief Get filtered set of VTs from an OSP server.
418 *
419 * @param[in] connection Connection to an OSP server.
420 * @param[in] opts Struct containing the options to apply.
421 * @param[out] vts VTs.
422 *
423 * @return 0 if success, 1 if error.
424 */
425 int
osp_get_vts_ext(osp_connection_t * connection,osp_get_vts_opts_t opts,entity_t * vts)426 osp_get_vts_ext (osp_connection_t *connection, osp_get_vts_opts_t opts,
427 entity_t *vts)
428 {
429 if (!connection)
430 return 1;
431
432 if (vts == NULL)
433 return 1;
434
435 if (opts.version_only == 1)
436 {
437 if (osp_send_command (connection, vts, "<get_vts version_only='1'/>"))
438 return 1;
439 return 0;
440 }
441
442 if (opts.filter)
443 {
444 if (osp_send_command (connection, vts, "<get_vts filter='%s'/>",
445 opts.filter))
446 return 1;
447 return 0;
448 }
449
450 if (osp_send_command (connection, vts, "<get_vts/>"))
451 return 1;
452 return 0;
453 }
454
455 /**
456 * @brief Delete a scan from an OSP server.
457 *
458 * @param[in] connection Connection to an OSP server.
459 * @param[in] scan_id ID of scan to delete.
460 *
461 * @return 0 if success, 1 if error.
462 */
463 int
osp_delete_scan(osp_connection_t * connection,const char * scan_id)464 osp_delete_scan (osp_connection_t *connection, const char *scan_id)
465 {
466 entity_t entity;
467 int ret = 0;
468 const char *status;
469
470 if (!connection)
471 return 1;
472
473 ret = osp_send_command (connection, &entity, "<delete_scan scan_id='%s'/>",
474 scan_id);
475 if (ret)
476 return 1;
477
478 /* Check response status. */
479 status = entity_attribute (entity, "status");
480 assert (status);
481 if (strcmp (status, "200"))
482 ret = 1;
483
484 free_entity (entity);
485 return ret;
486 }
487
488 /**
489 * @brief Get performance graphics from an OSP server.
490 *
491 * @param[in] connection Connection to an OSP server.
492 * @param[in] opts Struct containing the options to apply.
493 * @param[out] graph Graphic base64 encoded.
494 * @param[out] error Pointer to error, if any.
495 *
496 * @return 0 if success, -1 if error.
497 */
498 int
osp_get_performance_ext(osp_connection_t * connection,osp_get_performance_opts_t opts,char ** graph,char ** error)499 osp_get_performance_ext (osp_connection_t *connection,
500 osp_get_performance_opts_t opts, char **graph,
501 char **error)
502 {
503 entity_t entity;
504 int rc;
505 time_t now;
506
507 if (!connection)
508 {
509 if (error)
510 *error = g_strdup ("Couldn't send get_performance command "
511 "to scanner. Not valid connection");
512 return -1;
513 }
514
515 time (&now);
516
517 if (!opts.titles || !strcmp (opts.titles, "") || opts.start < 0
518 || opts.start > now || opts.end < 0 || opts.end > now)
519 {
520 if (error)
521 *error = g_strdup ("Couldn't send get_performance command "
522 "to scanner. Bad or missing parameters.");
523 return -1;
524 }
525
526 rc = osp_send_command (connection, &entity,
527 "<get_performance start='%d' "
528 "end='%d' titles='%s'/>",
529 opts.start, opts.end, opts.titles);
530
531 if (rc)
532 {
533 if (error)
534 *error = g_strdup ("Couldn't send get_performance command to scanner");
535 return -1;
536 }
537
538 if (graph && entity_text (entity) && strcmp (entity_text (entity), "\0"))
539 *graph = g_strdup (entity_text (entity));
540 else
541 {
542 const char *text = entity_attribute (entity, "status_text");
543
544 assert (text);
545 if (error)
546 *error = g_strdup (text);
547 free_entity (entity);
548 return -1;
549 }
550
551 free_entity (entity);
552 return 0;
553 }
554
555 /**
556 * @brief Get a scan status from an OSP server
557 *
558 * @param[in] connection Connection to an OSP server.
559 * @param[in] scan_id ID of scan to get.
560 * @param[out] error Pointer to error, if any.
561 *
562 * @return Osp scan status
563 */
564 osp_scan_status_t
osp_get_scan_status_ext(osp_connection_t * connection,osp_get_scan_status_opts_t opts,char ** error)565 osp_get_scan_status_ext (osp_connection_t *connection,
566 osp_get_scan_status_opts_t opts, char **error)
567 {
568 entity_t entity, child;
569 int rc;
570 osp_scan_status_t status = OSP_SCAN_STATUS_ERROR;
571
572 if (!connection)
573 {
574 if (error)
575 *error = g_strdup ("Couldn't send get_scans command "
576 "to scanner. Not valid connection");
577 return status;
578 }
579
580 assert (opts.scan_id);
581 rc = osp_send_command (connection, &entity,
582 "<get_scans scan_id='%s'"
583 " details='0'"
584 " pop_results='0'/>",
585 opts.scan_id);
586
587 if (rc)
588 {
589 if (error)
590 *error = g_strdup ("Couldn't send get_scans command to scanner");
591 return status;
592 }
593
594 child = entity_child (entity, "scan");
595 if (!child)
596 {
597 const char *text = entity_attribute (entity, "status_text");
598
599 assert (text);
600 if (error)
601 *error = g_strdup (text);
602 free_entity (entity);
603 return status;
604 }
605
606 if (!strcmp (entity_attribute (child, "status"), "queued"))
607 status = OSP_SCAN_STATUS_QUEUED;
608 else if (!strcmp (entity_attribute (child, "status"), "init"))
609 status = OSP_SCAN_STATUS_INIT;
610 else if (!strcmp (entity_attribute (child, "status"), "running"))
611 status = OSP_SCAN_STATUS_RUNNING;
612 else if (!strcmp (entity_attribute (child, "status"), "stopped"))
613 status = OSP_SCAN_STATUS_STOPPED;
614 else if (!strcmp (entity_attribute (child, "status"), "finished"))
615 status = OSP_SCAN_STATUS_FINISHED;
616 else if (!strcmp (entity_attribute (child, "status"), "interrupted"))
617 status = OSP_SCAN_STATUS_INTERRUPTED;
618
619 free_entity (entity);
620 return status;
621 }
622
623 /**
624 * @brief Get a scan from an OSP server, optionally removing the results.
625 *
626 * @param[in] connection Connection to an OSP server.
627 * @param[in] scan_id ID of scan to get.
628 * @param[out] report_xml Scans report.
629 * @param[in] details 0 for no scan details, 1 otherwise.
630 * @param[in] pop_results 0 to leave results, 1 to pop results from scanner.
631 * @param[out] error Pointer to error, if any.
632 *
633 * @return Scan progress if success, -1 if error.
634 */
635 int
osp_get_scan_pop(osp_connection_t * connection,const char * scan_id,char ** report_xml,int details,int pop_results,char ** error)636 osp_get_scan_pop (osp_connection_t *connection, const char *scan_id,
637 char **report_xml, int details, int pop_results, char **error)
638 {
639 entity_t entity, child;
640 int progress;
641 int rc;
642
643 if (!connection)
644 {
645 if (error)
646 *error = g_strdup ("Couldn't send get_scan command "
647 "to scanner. Not valid connection");
648 return -1;
649 }
650 assert (scan_id);
651 rc = osp_send_command (connection, &entity,
652 "<get_scans scan_id='%s'"
653 " details='%d'"
654 " pop_results='%d'/>",
655 scan_id, pop_results ? 1 : 0, details ? 1 : 0);
656 if (rc)
657 {
658 if (error)
659 *error = g_strdup ("Couldn't send get_scans command to scanner");
660 return -1;
661 }
662
663 child = entity_child (entity, "scan");
664 if (!child)
665 {
666 const char *text = entity_attribute (entity, "status_text");
667
668 assert (text);
669 if (error)
670 *error = g_strdup (text);
671 free_entity (entity);
672 return -1;
673 }
674 progress = atoi (entity_attribute (child, "progress"));
675 if (report_xml)
676 {
677 GString *string;
678
679 string = g_string_new ("");
680 print_entity_to_string (child, string);
681 *report_xml = g_string_free (string, FALSE);
682 }
683 free_entity (entity);
684 return progress;
685 }
686
687 /**
688 * @brief Get a scan from an OSP server.
689 *
690 * @param[in] connection Connection to an OSP server.
691 * @param[in] scan_id ID of scan to get.
692 * @param[out] report_xml Scans report.
693 * @param[in] details 0 for no scan details, 1 otherwise.
694 * @param[out] error Pointer to error, if any.
695 *
696 * @return Scan progress if success, -1 if error.
697 */
698 int
osp_get_scan(osp_connection_t * connection,const char * scan_id,char ** report_xml,int details,char ** error)699 osp_get_scan (osp_connection_t *connection, const char *scan_id,
700 char **report_xml, int details, char **error)
701 {
702 return osp_get_scan_pop (connection, scan_id, report_xml, details, 0, error);
703 }
704
705 /**
706 * @brief Stop a scan on an OSP server.
707 *
708 * @param[in] connection Connection to an OSP server.
709 * @param[in] scan_id ID of scan to delete.
710 * @param[out] error Pointer to error, if any.
711 *
712 * @return Scan progress if success, -1 if error.
713 */
714 int
osp_stop_scan(osp_connection_t * connection,const char * scan_id,char ** error)715 osp_stop_scan (osp_connection_t *connection, const char *scan_id, char **error)
716 {
717 entity_t entity;
718 int rc;
719
720 if (!connection)
721 {
722 if (error)
723 *error = g_strdup ("Couldn't send stop_scan command "
724 "to scanner. Not valid connection");
725 return -1;
726 }
727 assert (scan_id);
728 rc = osp_send_command (connection, &entity, "<stop_scan scan_id='%s'/>",
729 scan_id);
730 if (rc)
731 {
732 if (error)
733 *error = g_strdup ("Couldn't send stop_scan command to scanner");
734 return -1;
735 }
736
737 rc = atoi (entity_attribute (entity, "status"));
738 if (rc == 200)
739 {
740 free_entity (entity);
741 return 0;
742 }
743 else
744 {
745 const char *text = entity_attribute (entity, "status_text");
746
747 assert (text);
748 if (error)
749 *error = g_strdup (text);
750 free_entity (entity);
751 return -1;
752 }
753 }
754
755 /**
756 * @brief Concatenate options as xml.
757 *
758 * @param[in] key Tag name for xml element.
759 * @param[in] value Text for xml element.
760 * @param[in,out] pstr Parameters as xml concatenated xml elements.
761 *
762 */
763 static void
option_concat_as_xml(gpointer key,gpointer value,gpointer pstr)764 option_concat_as_xml (gpointer key, gpointer value, gpointer pstr)
765 {
766 char *options_str, *tmp, *key_escaped, *value_escaped;
767
768 options_str = *(char **) pstr;
769
770 key_escaped = g_markup_escape_text ((char *) key, -1);
771 value_escaped = g_markup_escape_text ((char *) value, -1);
772 tmp = g_strdup_printf ("%s<%s>%s</%s>", options_str ? options_str : "",
773 key_escaped, value_escaped, key_escaped);
774
775 g_free (options_str);
776 g_free (key_escaped);
777 g_free (value_escaped);
778 *(char **) pstr = tmp;
779 }
780
781 /**
782 * @brief Start an OSP scan against a target.
783 *
784 * @param[in] connection Connection to an OSP server.
785 * @param[in] target Target host to scan.
786 * @param[in] ports List of ports to scan.
787 * @param[in] options Table of scan options.
788 * @param[in] scan_id uuid to set for scan, null otherwise.
789 * @param[out] error Pointer to error, if any.
790 *
791 * @return 0 on success, -1 otherwise.
792 */
793 int
osp_start_scan(osp_connection_t * connection,const char * target,const char * ports,GHashTable * options,const char * scan_id,char ** error)794 osp_start_scan (osp_connection_t *connection, const char *target,
795 const char *ports, GHashTable *options, const char *scan_id,
796 char **error)
797 {
798 entity_t entity;
799 char *options_str = NULL;
800 int status;
801 int rc;
802
803 if (!connection)
804 {
805 if (error)
806 *error = g_strdup ("Couldn't send start_scan command "
807 "to scanner. Not valid connection");
808 return -1;
809 }
810
811 assert (target);
812 /* Construct options string. */
813 if (options)
814 g_hash_table_foreach (options, option_concat_as_xml, &options_str);
815
816 rc = osp_send_command (connection, &entity,
817 "<start_scan target='%s' ports='%s' scan_id='%s'>"
818 "<scanner_params>%s</scanner_params></start_scan>",
819 target, ports ? ports : "", scan_id ? scan_id : "",
820 options_str ? options_str : "");
821 g_free (options_str);
822 if (rc)
823 {
824 if (error)
825 *error = g_strdup ("Couldn't send start_scan command to scanner");
826 return -1;
827 }
828
829 status = atoi (entity_attribute (entity, "status"));
830 if (status == 200)
831 {
832 free_entity (entity);
833 return 0;
834 }
835 else
836 {
837 const char *text = entity_attribute (entity, "status_text");
838
839 assert (text);
840 if (error)
841 *error = g_strdup (text);
842 free_entity (entity);
843 return -1;
844 }
845 }
846
847 /**
848 * @brief Concatenate a credential as XML.
849 *
850 * @param[in] credential Credential data.
851 * @param[in,out] xml_string XML string buffer to append to.
852 *
853 */
854 static void
credential_append_as_xml(osp_credential_t * credential,GString * xml_string)855 credential_append_as_xml (osp_credential_t *credential, GString *xml_string)
856
857 {
858 GHashTableIter auth_data_iter;
859 gchar *auth_data_name, *auth_data_value;
860
861 xml_string_append (xml_string,
862 "<credential type=\"%s\" service=\"%s\" port=\"%s\">",
863 credential->type ? credential->type : "",
864 credential->service ? credential->service : "",
865 credential->port ? credential->port : "");
866
867 g_hash_table_iter_init (&auth_data_iter, credential->auth_data);
868 while (g_hash_table_iter_next (&auth_data_iter, (gpointer *) &auth_data_name,
869 (gpointer *) &auth_data_value))
870 {
871 xml_string_append (xml_string, "<%s>%s</%s>", auth_data_name,
872 auth_data_value, auth_data_name);
873 }
874
875 xml_string_append (xml_string, "</credential>");
876 }
877
878 /**
879 * @brief Concatenate a target as XML.
880 *
881 * @param[in] target Target data.
882 * @param[in,out] xml_string XML string buffer to append to.
883 *
884 */
885 static void
target_append_as_xml(osp_target_t * target,GString * xml_string)886 target_append_as_xml (osp_target_t *target, GString *xml_string)
887 {
888 xml_string_append (xml_string,
889 "<target>"
890 "<hosts>%s</hosts>"
891 "<exclude_hosts>%s</exclude_hosts>"
892 "<finished_hosts>%s</finished_hosts>"
893 "<ports>%s</ports>",
894 target->hosts ? target->hosts : "",
895 target->exclude_hosts ? target->exclude_hosts : "",
896 target->finished_hosts ? target->finished_hosts : "",
897 target->ports ? target->ports : "");
898
899 /* Alive test specified as bitfield */
900 if (target->alive_test > 0)
901 xml_string_append (xml_string, "<alive_test>%d</alive_test>",
902 target->alive_test);
903 /* Alive test specified via dedicated methods. Dedicted methods are ignored if
904 * alive test was already specified as bitfield.*/
905 else if (target->icmp == TRUE || target->tcp_syn == TRUE
906 || target->tcp_ack == TRUE || target->arp == TRUE
907 || target->consider_alive == TRUE)
908 {
909 xml_string_append (xml_string,
910 "<alive_test_methods>"
911 "<icmp>%d</icmp>"
912 "<tcp_syn>%d</tcp_syn>"
913 "<tcp_ack>%d</tcp_ack>"
914 "<arp>%d</arp>"
915 "<consider_alive>%d</consider_alive>"
916 "</alive_test_methods>",
917 target->icmp, target->tcp_syn, target->tcp_ack,
918 target->arp, target->consider_alive);
919 }
920
921 if (target->reverse_lookup_unify == 1)
922 xml_string_append (xml_string,
923 "<reverse_lookup_unify>%d</reverse_lookup_unify>",
924 target->reverse_lookup_unify);
925 if (target->reverse_lookup_only == 1)
926 xml_string_append (xml_string,
927 "<reverse_lookup_only>%d</reverse_lookup_only>",
928 target->reverse_lookup_only);
929
930 if (target->credentials)
931 {
932 g_string_append (xml_string, "<credentials>");
933 g_slist_foreach (target->credentials, (GFunc) credential_append_as_xml,
934 xml_string);
935 g_string_append (xml_string, "</credentials>");
936 }
937 xml_string_append (xml_string, "</target>");
938 }
939
940 /**
941 * @brief Append VT groups as XML to a string buffer.
942 *
943 * @param[in] vt_group VT group data.
944 * @param[in,out] xml_string XML string buffer to append to.
945 */
946 static void
vt_group_append_as_xml(osp_vt_group_t * vt_group,GString * xml_string)947 vt_group_append_as_xml (osp_vt_group_t *vt_group, GString *xml_string)
948 {
949 xml_string_append (xml_string, "<vt_group filter=\"%s\"/>", vt_group->filter);
950 }
951
952 /**
953 * @brief Append VT values as XML to a string buffer.
954 *
955 * @param[in] id Identifier of the vt_value.
956 * @param[in] value The value of the vt_value.
957 * @param[in,out] xml_string XML string buffer to append to.
958 *
959 */
960 static void
vt_value_append_as_xml(gpointer id,gchar * value,GString * xml_string)961 vt_value_append_as_xml (gpointer id, gchar *value, GString *xml_string)
962 {
963 xml_string_append (xml_string, "<vt_value id=\"%s\">%s</vt_value>",
964 id ? id : "", value ? value : "");
965 }
966
967 /**
968 * @brief Append single VTs as XML to a string buffer.
969 *
970 * @param[in] vt_single Single VT data.
971 * @param[in,out] xml_string XML string buffer to append to.
972 */
973 static void
vt_single_append_as_xml(osp_vt_single_t * vt_single,GString * xml_string)974 vt_single_append_as_xml (osp_vt_single_t *vt_single, GString *xml_string)
975 {
976 xml_string_append (xml_string, "<vt_single id=\"%s\">", vt_single->vt_id);
977 g_hash_table_foreach (vt_single->vt_values, (GHFunc) vt_value_append_as_xml,
978 xml_string);
979 xml_string_append (xml_string, "</vt_single>");
980 }
981
982 /**
983 * @brief Start an OSP scan against a target.
984 *
985 * @param[in] connection Connection to an OSP server.
986 * @param[in] opts Struct containing the options to apply.
987 * @param[out] error Pointer to error, if any.
988 *
989 * @return 0 on success, -1 otherwise.
990 */
991 int
osp_start_scan_ext(osp_connection_t * connection,osp_start_scan_opts_t opts,char ** error)992 osp_start_scan_ext (osp_connection_t *connection, osp_start_scan_opts_t opts,
993 char **error)
994 {
995 gchar *scanner_params_xml = NULL;
996 GString *xml;
997 GSList *list_item;
998 int list_count;
999 int rc, status;
1000 entity_t entity;
1001 gchar *cmd;
1002 char filename[] = "/tmp/osp-cmd-XXXXXX";
1003 int fd;
1004
1005 if (!connection)
1006 {
1007 if (error)
1008 *error = g_strdup ("Couldn't send start_scan command "
1009 "to scanner. Not valid connection");
1010 return -1;
1011 }
1012
1013 fd = mkstemp (filename);
1014 FILE *file = fdopen (fd, "w");
1015
1016 xml = g_string_sized_new (10240);
1017 g_string_append (xml, "<start_scan");
1018 xml_string_append (xml, " scan_id=\"%s\">", opts.scan_id ? opts.scan_id : "");
1019
1020 g_string_append (xml, "<targets>");
1021 g_slist_foreach (opts.targets, (GFunc) target_append_as_xml, xml);
1022 g_string_append (xml, "</targets>");
1023
1024 g_string_append (xml, "<scanner_params>");
1025 if (opts.scanner_params)
1026 {
1027 scanner_params_xml = NULL;
1028 g_hash_table_foreach (opts.scanner_params, (GHFunc) option_concat_as_xml,
1029 &scanner_params_xml);
1030 if (scanner_params_xml)
1031 g_string_append (xml, scanner_params_xml);
1032 g_free (scanner_params_xml);
1033 }
1034 g_string_append (xml, "</scanner_params>");
1035
1036 g_string_append (xml, "<vt_selection>");
1037 g_slist_foreach (opts.vt_groups, (GFunc) vt_group_append_as_xml, xml);
1038
1039 fprintf (file, "%s", xml->str);
1040
1041 g_string_free (xml, TRUE);
1042
1043 xml = g_string_new ("");
1044 list_item = opts.vts;
1045 list_count = 0;
1046 while (list_item)
1047 {
1048 list_count++;
1049 vt_single_append_as_xml (list_item->data, xml);
1050
1051 list_item = list_item->next;
1052
1053 if (list_count == 1000)
1054 {
1055 fprintf (file, "%s", xml->str);
1056
1057 g_string_free (xml, TRUE);
1058 xml = g_string_new ("");
1059 list_count = 0;
1060 }
1061 }
1062
1063 g_string_append (xml, "</vt_selection>");
1064 g_string_append (xml, "</start_scan>");
1065
1066 fprintf (file, "%s", xml->str);
1067 fflush (file);
1068 fclose (file);
1069 g_string_free (xml, TRUE);
1070
1071 g_file_get_contents (filename, &cmd, NULL, NULL);
1072
1073 rc = osp_send_command (connection, &entity, "%s", cmd);
1074
1075 g_free (cmd);
1076 unlink (filename);
1077
1078 if (rc)
1079 {
1080 if (error)
1081 *error = g_strdup ("Could not send start_scan command to scanner");
1082 return -1;
1083 }
1084
1085 status = atoi (entity_attribute (entity, "status"));
1086 if (status == 200)
1087 {
1088 free_entity (entity);
1089 return 0;
1090 }
1091 else
1092 {
1093 const char *text = entity_attribute (entity, "status_text");
1094
1095 assert (text);
1096 if (error)
1097 *error = g_strdup (text);
1098 free_entity (entity);
1099 return -1;
1100 }
1101
1102 if (error)
1103 *error = NULL;
1104 free_entity (entity);
1105 return 0;
1106 }
1107
1108 /**
1109 * @brief Get an OSP parameter's type from its string format.
1110 *
1111 * @param[in] str OSP parameter in string format.
1112 *
1113 * @return OSP parameter type.
1114 */
1115 static osp_param_type_t
osp_param_str_to_type(const char * str)1116 osp_param_str_to_type (const char *str)
1117 {
1118 assert (str);
1119 if (!strcmp (str, "integer"))
1120 return OSP_PARAM_TYPE_INT;
1121 else if (!strcmp (str, "string"))
1122 return OSP_PARAM_TYPE_STR;
1123 else if (!strcmp (str, "password"))
1124 return OSP_PARAM_TYPE_PASSWORD;
1125 else if (!strcmp (str, "file"))
1126 return OSP_PARAM_TYPE_FILE;
1127 else if (!strcmp (str, "boolean"))
1128 return OSP_PARAM_TYPE_BOOLEAN;
1129 else if (!strcmp (str, "ovaldef_file"))
1130 return OSP_PARAM_TYPE_OVALDEF_FILE;
1131 else if (!strcmp (str, "selection"))
1132 return OSP_PARAM_TYPE_SELECTION;
1133 else if (!strcmp (str, "credential_up"))
1134 return OSP_PARAM_TYPE_CRD_UP;
1135 assert (0);
1136 return 0;
1137 }
1138
1139 /**
1140 * @brief Get an OSP parameter in string format form its type.
1141 *
1142 * @param[in] param OSP parameter.
1143 *
1144 * @return OSP parameter in string format.
1145 */
1146 const char *
osp_param_type_str(const osp_param_t * param)1147 osp_param_type_str (const osp_param_t *param)
1148 {
1149 osp_param_type_t type;
1150
1151 assert (param);
1152 type = param->type;
1153 if (type == OSP_PARAM_TYPE_INT)
1154 return "integer";
1155 else if (type == OSP_PARAM_TYPE_STR)
1156 return "string";
1157 else if (type == OSP_PARAM_TYPE_PASSWORD)
1158 return "password";
1159 else if (type == OSP_PARAM_TYPE_FILE)
1160 return "file";
1161 else if (type == OSP_PARAM_TYPE_BOOLEAN)
1162 return "boolean";
1163 else if (type == OSP_PARAM_TYPE_OVALDEF_FILE)
1164 return "ovaldef_file";
1165 else if (type == OSP_PARAM_TYPE_SELECTION)
1166 return "selection";
1167 else if (type == OSP_PARAM_TYPE_CRD_UP)
1168 return "credential_up";
1169 assert (0);
1170 return NULL;
1171 }
1172
1173 /**
1174 * @brief Get an OSP scanner's details.
1175 *
1176 * @param[in] connection Connection to an OSP server.
1177 * @param[out] desc Scanner's description.
1178 * @param[out] params Scanner's parameters.
1179 *
1180 * @return 0 if success, 1 if failure.
1181 */
1182 int
osp_get_scanner_details(osp_connection_t * connection,char ** desc,GSList ** params)1183 osp_get_scanner_details (osp_connection_t *connection, char **desc,
1184 GSList **params)
1185 {
1186 entity_t entity, child;
1187 entities_t entities;
1188
1189 assert (connection);
1190
1191 if (osp_send_command (connection, &entity, "<get_scanner_details/>"))
1192 return 1;
1193 if (params)
1194 {
1195 child = entity_child (entity, "scanner_params");
1196 if (!child)
1197 {
1198 free_entity (entity);
1199 return 1;
1200 }
1201 entities = child->entities;
1202 while (entities)
1203 {
1204 osp_param_t *param;
1205
1206 child = entities->data;
1207 param = osp_param_new ();
1208 param->id = g_strdup (entity_attribute (child, "id"));
1209 param->type =
1210 osp_param_str_to_type (entity_attribute (child, "type"));
1211 param->name = g_strdup (entity_text (entity_child (child, "name")));
1212 param->desc =
1213 g_strdup (entity_text (entity_child (child, "description")));
1214 param->def = g_strdup (entity_text (entity_child (child, "default")));
1215 if (entity_child (child, "mandatory"))
1216 param->mandatory =
1217 atoi (entity_text (entity_child (child, "mandatory")));
1218 *params = g_slist_append (*params, param);
1219 entities = next_entities (entities);
1220 }
1221 }
1222 if (desc)
1223 {
1224 child = entity_child (entity, "description");
1225 assert (child);
1226 *desc = g_strdup (entity_text (child));
1227 }
1228
1229 free_entity (entity);
1230 return 0;
1231 }
1232
1233 /**
1234 * @brief Create a new OSP parameter.
1235 *
1236 * @return New OSP parameter.
1237 */
1238 osp_param_t *
osp_param_new(void)1239 osp_param_new (void)
1240 {
1241 return g_malloc0 (sizeof (osp_param_t));
1242 }
1243
1244 /**
1245 * @brief Get an OSP parameter's id.
1246 *
1247 * @param[in] param OSP parameter.
1248 *
1249 * @return ID of OSP parameter.
1250 */
1251 const char *
osp_param_id(const osp_param_t * param)1252 osp_param_id (const osp_param_t *param)
1253 {
1254 assert (param);
1255
1256 return param->id;
1257 }
1258
1259 /**
1260 * @brief Get an OSP parameter's name.
1261 *
1262 * @param[in] param OSP parameter.
1263 *
1264 * @return Name of OSP parameter.
1265 */
1266 const char *
osp_param_name(const osp_param_t * param)1267 osp_param_name (const osp_param_t *param)
1268 {
1269 assert (param);
1270
1271 return param->name;
1272 }
1273
1274 /**
1275 * @brief Get an OSP parameter's description.
1276 *
1277 * @param[in] param OSP parameter.
1278 *
1279 * @return Description of OSP parameter.
1280 */
1281 const char *
osp_param_desc(const osp_param_t * param)1282 osp_param_desc (const osp_param_t *param)
1283 {
1284 assert (param);
1285
1286 return param->desc;
1287 }
1288
1289 /**
1290 * @brief Get an OSP parameter's default value.
1291 *
1292 * @param[in] param OSP parameter.
1293 *
1294 * @return Default value of OSP parameter.
1295 */
1296 const char *
osp_param_default(const osp_param_t * param)1297 osp_param_default (const osp_param_t *param)
1298 {
1299 assert (param);
1300
1301 return param->def;
1302 }
1303
1304 /**
1305 * @brief Get an OSP parameter's mandatory value.
1306 *
1307 * @param[in] param OSP parameter.
1308 *
1309 * @return Mandatory value of OSP parameter.
1310 */
1311 int
osp_param_mandatory(const osp_param_t * param)1312 osp_param_mandatory (const osp_param_t *param)
1313 {
1314 assert (param);
1315
1316 return param->mandatory;
1317 }
1318
1319 /**
1320 * @brief Free an OSP parameter.
1321 *
1322 * @param[in] param OSP parameter to destroy.
1323 */
1324 void
osp_param_free(osp_param_t * param)1325 osp_param_free (osp_param_t *param)
1326 {
1327 if (!param)
1328 return;
1329 g_free (param->id);
1330 g_free (param->name);
1331 g_free (param->desc);
1332 g_free (param->def);
1333 g_free (param);
1334 }
1335
1336 /**
1337 * @brief Allocate and initialize a new OSP credential.
1338 *
1339 * @param[in] type The credential type.
1340 * @param[in] service The service the credential is for.
1341 * @param[in] port The port.
1342 *
1343 * @return New osp credential.
1344 */
1345 osp_credential_t *
osp_credential_new(const char * type,const char * service,const char * port)1346 osp_credential_new (const char *type, const char *service, const char *port)
1347 {
1348 osp_credential_t *new_credential;
1349
1350 new_credential = g_malloc0 (sizeof (osp_credential_t));
1351
1352 new_credential->type = type ? g_strdup (type) : NULL;
1353 new_credential->service = service ? g_strdup (service) : NULL;
1354 new_credential->port = port ? g_strdup (port) : NULL;
1355 new_credential->auth_data =
1356 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1357
1358 return new_credential;
1359 }
1360
1361 /**
1362 * @brief Free an OSP credential.
1363 *
1364 * @param[in] credential The credential to free.
1365 */
1366 void
osp_credential_free(osp_credential_t * credential)1367 osp_credential_free (osp_credential_t *credential)
1368 {
1369 if (!credential)
1370 return;
1371
1372 g_free (credential->type);
1373 g_free (credential->service);
1374 g_free (credential->port);
1375 g_hash_table_destroy (credential->auth_data);
1376 g_free (credential);
1377 }
1378
1379 /**
1380 * @brief Get authentication data from an OSP credential.
1381 *
1382 * @param[in] credential The credential to get the data from.
1383 * @param[in] name The name of the data item to get.
1384 *
1385 * @return The requested authentication data or NULL if not available.
1386 */
1387 const gchar *
osp_credential_get_auth_data(osp_credential_t * credential,const char * name)1388 osp_credential_get_auth_data (osp_credential_t *credential, const char *name)
1389 {
1390 if (credential == NULL || name == NULL)
1391 return NULL;
1392 return g_hash_table_lookup (credential->auth_data, name);
1393 }
1394
1395 /**
1396 * @brief Get authentication data from an OSP credential.
1397 *
1398 * @param[in] credential The credential to get the data from.
1399 * @param[in] name The name of the data item to get.
1400 * @param[in] value The authentication data or NULL to unset.
1401 */
1402 void
osp_credential_set_auth_data(osp_credential_t * credential,const char * name,const char * value)1403 osp_credential_set_auth_data (osp_credential_t *credential, const char *name,
1404 const char *value)
1405 {
1406 if (credential == NULL || name == NULL)
1407 return;
1408
1409 if (g_regex_match_simple ("^[[:alpha:]][[:alnum:]_]*$", name, 0, 0))
1410 {
1411 if (value)
1412 g_hash_table_replace (credential->auth_data, g_strdup (name),
1413 g_strdup (value));
1414 else
1415 g_hash_table_remove (credential->auth_data, name);
1416 }
1417 else
1418 {
1419 g_warning ("%s: Invalid auth data name: %s", __func__, name);
1420 }
1421 }
1422
1423 /**
1424 * @brief Create a new OSP target.
1425 *
1426 * @param[in] hosts The hostnames of the target.
1427 * @param[in] ports The ports of the target.
1428 * @param[in] exclude_hosts The excluded hosts of the target.
1429 * @param[in] alive_test The alive test method of the target.
1430 *
1431 * @return The newly allocated osp_target_t.
1432 */
1433 osp_target_t *
osp_target_new(const char * hosts,const char * ports,const char * exclude_hosts,int alive_test,int reverse_lookup_unify,int reverse_lookup_only)1434 osp_target_new (const char *hosts, const char *ports, const char *exclude_hosts,
1435 int alive_test, int reverse_lookup_unify,
1436 int reverse_lookup_only)
1437 {
1438 osp_target_t *new_target;
1439 new_target = g_malloc0 (sizeof (osp_target_t));
1440
1441 new_target->exclude_hosts = exclude_hosts ? g_strdup (exclude_hosts) : NULL;
1442 new_target->hosts = hosts ? g_strdup (hosts) : NULL;
1443 new_target->ports = ports ? g_strdup (ports) : NULL;
1444 new_target->finished_hosts = NULL;
1445 new_target->alive_test = alive_test ? alive_test : 0;
1446 new_target->reverse_lookup_unify =
1447 reverse_lookup_unify ? reverse_lookup_unify : 0;
1448 new_target->reverse_lookup_only =
1449 reverse_lookup_only ? reverse_lookup_only : 0;
1450
1451 return new_target;
1452 }
1453
1454 /**
1455 * @brief Set the finished hosts of an OSP target.
1456 *
1457 * @param[in] target The OSP target to modify.
1458 * @param[in] finished_hosts The hostnames to consider finished.
1459 */
1460 void
osp_target_set_finished_hosts(osp_target_t * target,const char * finished_hosts)1461 osp_target_set_finished_hosts (osp_target_t *target, const char *finished_hosts)
1462 {
1463 g_free (target->finished_hosts);
1464 target->finished_hosts = finished_hosts ? g_strdup (finished_hosts) : NULL;
1465 }
1466
1467 /**
1468 * @brief Free an OSP target, including all added credentials.
1469 *
1470 * @param[in] target The OSP target to free.
1471 */
1472 void
osp_target_free(osp_target_t * target)1473 osp_target_free (osp_target_t *target)
1474 {
1475 if (!target)
1476 return;
1477
1478 g_slist_free_full (target->credentials, (GDestroyNotify) osp_credential_free);
1479 g_free (target->exclude_hosts);
1480 g_free (target->hosts);
1481 g_free (target->ports);
1482 g_free (target);
1483 }
1484
1485 /**
1486 * @brief Add alive test methods to OSP target.
1487 *
1488 * @param[in] target The OSP target to add the methods to.
1489 * @param[in] icmp Use ICMP ping.
1490 * @param[in] tcp_syn Use TCP-SYN ping.
1491 * @param[in] tcp_ack Use TCP-ACK ping.
1492 * @param[in] arp Use ARP ping.
1493 * @param[in] consider_alive Consider host to be alive.
1494 */
1495 void
osp_target_add_alive_test_methods(osp_target_t * target,gboolean icmp,gboolean tcp_syn,gboolean tcp_ack,gboolean arp,gboolean consider_alive)1496 osp_target_add_alive_test_methods (osp_target_t *target, gboolean icmp,
1497 gboolean tcp_syn, gboolean tcp_ack,
1498 gboolean arp, gboolean consider_alive)
1499 {
1500 if (!target)
1501 return;
1502
1503 target->icmp = icmp;
1504 target->tcp_syn = tcp_syn;
1505 target->tcp_ack = tcp_ack;
1506 target->arp = arp;
1507 target->consider_alive = consider_alive;
1508 }
1509
1510 /**
1511 * @brief Add a credential to an OSP target.
1512 *
1513 * @param[in] target The OSP target to add the credential to.
1514 * @param[in] credential The credential to add. Will be freed with target.
1515 */
1516 void
osp_target_add_credential(osp_target_t * target,osp_credential_t * credential)1517 osp_target_add_credential (osp_target_t *target, osp_credential_t *credential)
1518 {
1519 if (!target || !credential)
1520 return;
1521
1522 target->credentials = g_slist_prepend (target->credentials, credential);
1523 }
1524
1525 /**
1526 * @brief Create a new OSP VT group.
1527 *
1528 * @param[in] filter The filter string for the VT group.
1529 *
1530 * @return The newly allocated VT group.
1531 */
1532 osp_vt_group_t *
osp_vt_group_new(const char * filter)1533 osp_vt_group_new (const char *filter)
1534 {
1535 osp_vt_group_t *new_vt_group;
1536 new_vt_group = g_malloc0 (sizeof (osp_vt_group_t));
1537
1538 new_vt_group->filter = filter ? g_strdup (filter) : NULL;
1539
1540 return new_vt_group;
1541 }
1542
1543 /**
1544 * @brief Free a OSP VT group.
1545 *
1546 * @param[in] vt_group The VT group to free.
1547 */
1548 void
osp_vt_group_free(osp_vt_group_t * vt_group)1549 osp_vt_group_free (osp_vt_group_t *vt_group)
1550 {
1551 if (!vt_group)
1552 return;
1553
1554 g_free (vt_group->filter);
1555 g_free (vt_group);
1556 }
1557
1558 /**
1559 * @brief Create a new single OSP VT.
1560 *
1561 * @param[in] vt_id The id of the VT.
1562 *
1563 * @return The newly allocated single VT.
1564 */
1565 osp_vt_single_t *
osp_vt_single_new(const char * vt_id)1566 osp_vt_single_new (const char *vt_id)
1567 {
1568 osp_vt_single_t *new_vt_single;
1569 new_vt_single = g_malloc0 (sizeof (osp_vt_single_t));
1570
1571 new_vt_single->vt_id = vt_id ? g_strdup (vt_id) : NULL;
1572 new_vt_single->vt_values =
1573 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1574
1575 return new_vt_single;
1576 }
1577
1578 /**
1579 * @brief Free a single OSP VT, including all preference values.
1580 *
1581 * @param[in] vt_single The OSP VT to free.
1582 */
1583 void
osp_vt_single_free(osp_vt_single_t * vt_single)1584 osp_vt_single_free (osp_vt_single_t *vt_single)
1585 {
1586 if (!vt_single)
1587 return;
1588
1589 g_hash_table_destroy (vt_single->vt_values);
1590
1591 g_free (vt_single->vt_id);
1592 g_free (vt_single);
1593 }
1594
1595 /**
1596 * @brief Add a preference value to an OSP VT.
1597 * This creates a copy of the name and value.
1598 *
1599 * @param[in] vt_single The VT to add the preference to.
1600 * @param[in] name The name / identifier of the preference.
1601 * @param[in] value The value of the preference.
1602 */
1603 void
osp_vt_single_add_value(osp_vt_single_t * vt_single,const char * name,const char * value)1604 osp_vt_single_add_value (osp_vt_single_t *vt_single, const char *name,
1605 const char *value)
1606 {
1607 g_hash_table_replace (vt_single->vt_values, g_strdup (name),
1608 g_strdup (value));
1609 }
1610