1 /* Copyright (C) 2018-2021 Greenbone Networks GmbH
2  *
3  * SPDX-License-Identifier: AGPL-3.0-or-later
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Affero General Public License as
7  * published by the Free Software Foundation, either version 3 of the
8  * 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 Affero General Public License for more details.
14  *
15  * You should have received a copy of the GNU Affero General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /**
20  * @file gmp_configs.c
21  * @brief GVM GMP layer: Configs
22  *
23  * GMP configs.
24  */
25 
26 #include "gmp_configs.h"
27 #include "gmp_base.h"
28 #include "gmp_get.h"
29 #include "manage_acl.h"
30 #include "manage_configs.h"
31 
32 #include <assert.h>
33 #include <glib.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include <gvm/util/xmlutils.h>
38 
39 #undef G_LOG_DOMAIN
40 /**
41  * @brief GLib log domain.
42  */
43 #define G_LOG_DOMAIN "md    gmp"
44 
45 
46 /* Helpers. */
47 
48 /**
49  * @brief Create a new NVT selector.
50  *
51  * @param[in]  name           Name of NVT selector.
52  * @param[in]  type           Type of NVT selector.
53  * @param[in]  include        Include/exclude flag.
54  * @param[in]  family_or_nvt  Family or NVT.
55  *
56  * @return Newly allocated NVT selector.
57  */
58 static gpointer
nvt_selector_new(char * name,char * type,int include,char * family_or_nvt)59 nvt_selector_new (char *name, char *type, int include, char *family_or_nvt)
60 {
61   nvt_selector_t *selector;
62 
63   selector = (nvt_selector_t*) g_malloc0 (sizeof (nvt_selector_t));
64   selector->name = name;
65   selector->type = type;
66   selector->include = include;
67   selector->family_or_nvt = family_or_nvt;
68 
69   return selector;
70 }
71 
72 
73 /* CREATE_CONFIG. */
74 
75 /**
76  * @brief The create_config command.
77  */
78 typedef struct
79 {
80   context_data_t *context;     ///< XML parser context.
81 } create_config_t;
82 
83 /**
84  * @brief Parser callback data.
85  *
86  * This is initially 0 because it's a global variable.
87  */
88 static create_config_t create_config_data;
89 
90 /**
91  * @brief Reset command data.
92  */
93 static void
create_config_reset()94 create_config_reset ()
95 {
96   if (create_config_data.context->first)
97     {
98       free_entity (create_config_data.context->first->data);
99       g_slist_free_1 (create_config_data.context->first);
100     }
101   g_free (create_config_data.context);
102   memset (&create_config_data, 0, sizeof (create_config_t));
103 }
104 
105 /**
106  * @brief Start a command.
107  *
108  * @param[in]  gmp_parser        GMP parser.
109  * @param[in]  attribute_names   All attribute names.
110  * @param[in]  attribute_values  All attribute values.
111  */
112 void
create_config_start(gmp_parser_t * gmp_parser,const gchar ** attribute_names,const gchar ** attribute_values)113 create_config_start (gmp_parser_t *gmp_parser,
114                      const gchar **attribute_names,
115                      const gchar **attribute_values)
116 {
117   memset (&create_config_data, 0, sizeof (create_config_t));
118   create_config_data.context = g_malloc0 (sizeof (context_data_t));
119   create_config_element_start (gmp_parser, "create_config", attribute_names,
120                                attribute_values);
121 }
122 
123 /**
124  * @brief Start element.
125  *
126  * @param[in]  gmp_parser        GMP parser.
127  * @param[in]  name              Element name.
128  * @param[in]  attribute_names   All attribute names.
129  * @param[in]  attribute_values  All attribute values.
130  */
131 void
create_config_element_start(gmp_parser_t * gmp_parser,const gchar * name,const gchar ** attribute_names,const gchar ** attribute_values)132 create_config_element_start (gmp_parser_t *gmp_parser, const gchar *name,
133                              const gchar **attribute_names,
134                              const gchar **attribute_values)
135 {
136   xml_handle_start_element (create_config_data.context, name, attribute_names,
137                             attribute_values);
138 }
139 
140 /**
141  * @brief Get the text of entity.
142  *
143  * @param[in]  entity  Entity.  Can be NULL.
144  *
145  * @return Entity text if there's an entity, else NULL.
146  */
147 static gchar*
text_or_null(entity_t entity)148 text_or_null (entity_t entity)
149 {
150   if (entity
151       && strlen (entity_text (entity)))
152     return entity_text (entity);
153   return NULL;
154 }
155 
156 /**
157  * @brief Get the attribute of entity.
158  *
159  * @param[in]  entity  Entity.  Can be NULL.
160  * @param[in]  name    Name of attribute.
161  *
162  * @return Entity attribute if there's an entity, else NULL.
163  */
164 static gchar *
attr_or_null(entity_t entity,const gchar * name)165 attr_or_null (entity_t entity, const gchar *name)
166 {
167   assert (name);
168 
169   if (entity)
170     return (gchar*) entity_attribute (entity, name);
171   return NULL;
172 }
173 
174 /**
175  * @brief Get creation data from a config entity.
176  *
177  * @param[in]  config                Config entity.
178  * @param[out] config_id             Address for config ID, or NULL.
179  * @param[out] name                  Address for name.
180  * @param[out] comment               Address for comment.
181  * @param[out] type                  Address for type.
182  * @param[out] usage_type            Address for usage type.
183  * @param[out] all_selector          True if ALL_SELECTOR was present.
184  * @param[out] import_nvt_selectors  Address for selectors.
185  * @param[out] import_preferences    Address for preferences.
186  *
187  * @return 0 success, 1 preference did no exist, -1 preference without ID.
188  */
189 int
parse_config_entity(entity_t config,const char ** config_id,char ** name,char ** comment,char ** type,char ** usage_type,int * all_selector,array_t ** import_nvt_selectors,array_t ** import_preferences)190 parse_config_entity (entity_t config, const char **config_id, char **name,
191                      char **comment, char **type, char **usage_type,
192                      int *all_selector,
193                      array_t **import_nvt_selectors,
194                      array_t **import_preferences)
195 {
196   entity_t entity, preferences, nvt_selectors;
197 
198   *name = *comment = *type = NULL;
199   *all_selector = 0;
200 
201   if (config_id)
202     *config_id = entity_attribute (config, "id");
203 
204   entity = entity_child (config, "name");
205   if (entity)
206     *name = entity_text (entity);
207 
208   entity = entity_child (config, "comment");
209   if (entity)
210     *comment = entity_text (entity);
211 
212   entity = entity_child (config, "type");
213   if (entity)
214     *type = entity_text (entity);
215 
216   if (usage_type)
217     {
218       entity = entity_child (config, "usage_type");
219       if (entity)
220         *usage_type = entity_text (entity);
221       else
222         *usage_type = NULL;
223     }
224 
225   /* Collect NVT selectors. */
226 
227   *import_nvt_selectors = NULL;
228   nvt_selectors = entity_child (config, "nvt_selectors");
229   if (nvt_selectors)
230     {
231       entity_t nvt_selector;
232       entities_t children;
233 
234       *import_nvt_selectors = make_array ();
235       children = nvt_selectors->entities;
236       while ((nvt_selector = first_entity (children)))
237         {
238           entity_t include, selector_name, selector_type, selector_fam;
239           int import_include;
240 
241           if (strcmp (entity_name (nvt_selector), "all_selector") == 0)
242             {
243               array_free (*import_nvt_selectors);
244               *import_nvt_selectors = NULL;
245               *all_selector = 1;
246               break;
247             }
248 
249           include = entity_child (nvt_selector, "include");
250           if (include && strcmp (entity_text (include), "0") == 0)
251             import_include = 0;
252           else
253             import_include = 1;
254 
255           selector_name = entity_child (nvt_selector, "name");
256           selector_type = entity_child (nvt_selector, "type");
257           selector_fam = entity_child (nvt_selector, "family_or_nvt");
258 
259           array_add (*import_nvt_selectors,
260                      nvt_selector_new (text_or_null (selector_name),
261                                        text_or_null (selector_type),
262                                        import_include,
263                                        text_or_null (selector_fam)));
264 
265           children = next_entities (children);
266         }
267 
268       if (*import_nvt_selectors)
269         array_terminate (*import_nvt_selectors);
270     }
271 
272   /* Collect NVT preferences. */
273 
274   *import_preferences = NULL;
275   preferences = entity_child (config, "preferences");
276   if (preferences)
277     {
278       entity_t preference;
279       entities_t children;
280 
281       *import_preferences = make_array ();
282       children = preferences->entities;
283       while ((preference = first_entity (children)))
284         {
285           entity_t pref_name, pref_nvt_name, hr_name, nvt, alt;
286           char *preference_hr_name, *preference_nvt_oid;
287           array_t *import_alts;
288           entities_t alts;
289           preference_t *new_preference;
290 
291           pref_name = entity_child (preference, "name");
292 
293           pref_nvt_name = NULL;
294           nvt = entity_child (preference, "nvt");
295           if (nvt)
296             pref_nvt_name = entity_child (nvt, "name");
297 
298           hr_name = entity_child (preference, "hr_name");
299           if (*type == NULL || strcmp (*type, "0") == 0)
300             /* Classic OpenVAS config preference. */
301             preference_hr_name = NULL;
302           else if (hr_name && strlen (entity_text (hr_name)))
303             /* OSP config preference with hr_name given. */
304             preference_hr_name = entity_text (hr_name);
305           else
306             /* Old OSP config without hr_name. */
307             preference_hr_name = text_or_null (pref_name);
308 
309           import_alts = make_array ();
310           alts = preference->entities;
311           while ((alt = first_entity (alts)))
312             {
313               if (strcasecmp (entity_name (alt), "ALT") == 0)
314                 array_add (import_alts, text_or_null (alt));
315               alts = next_entities (alts);
316             }
317           array_terminate (import_alts);
318 
319           preference_nvt_oid = attr_or_null (nvt, "oid");
320 
321           if ((*type == NULL || strcmp (*type, "0") == 0)
322               && preference_nvt_oid
323               && strcmp (preference_nvt_oid, ""))
324             {
325               /* Preference in an OpenVAS config:
326                * Get the preference from nvt_preferences */
327               char *preference_id, *preference_name, *preference_type;
328               char *preference_value;
329 
330               preference_id
331                 = text_or_null (entity_child (preference, "id"));
332               preference_name
333                 = text_or_null (entity_child (preference, "name"));
334               preference_type
335                 = text_or_null (entity_child (preference, "type"));
336               preference_value
337                 = text_or_null (entity_child (preference, "value"));
338 
339               if (preference_id && strcmp (preference_id, ""))
340                 {
341                   new_preference
342                     = get_nvt_preference_by_id (preference_nvt_oid,
343                                                 preference_id,
344                                                 preference_name,
345                                                 preference_type,
346                                                 preference_value ?: "");
347                 }
348               else
349                 {
350                   g_warning ("%s: Config contains a preference for NVT %s"
351                              " without a preference id: %s",
352                              __func__,
353                              preference_nvt_oid,
354                              preference_name);
355 
356                   cleanup_import_preferences (*import_preferences);
357                   array_free (*import_nvt_selectors);
358 
359                   return -1;
360                 }
361             }
362           else
363             {
364               /* Scanner preference (for OpenVAS or OSP configs):
365                * Use directly from imported config.
366                */
367               new_preference
368                 = preference_new
369                       (text_or_null (entity_child (preference, "id")),
370                        text_or_null (pref_name),
371                        text_or_null (entity_child (preference, "type")),
372                        text_or_null (entity_child (preference, "value")),
373                        text_or_null (pref_nvt_name),
374                        preference_nvt_oid,
375                        import_alts,
376                        text_or_null (entity_child (preference, "default")),
377                        preference_hr_name,
378                        0 /* do not free strings */);
379             }
380 
381           array_add (*import_preferences, new_preference);
382 
383           children = next_entities (children);
384         }
385 
386       array_terminate (*import_preferences);
387     }
388 
389   return 0;
390 }
391 
392 /**
393  * @brief Execute command.
394  *
395  * @param[in]  gmp_parser   GMP parser.
396  * @param[in]  error        Error parameter.
397  */
398 void
create_config_run(gmp_parser_t * gmp_parser,GError ** error)399 create_config_run (gmp_parser_t *gmp_parser, GError **error)
400 {
401   entity_t entity, get_configs_response, config, name, copy, scanner;
402 
403   entity = (entity_t) create_config_data.context->first->data;
404 
405   /* For now the import element, GET_CONFIGS_RESPONSE, overrides
406    * any other elements. */
407 
408   get_configs_response = entity_child (entity, "get_configs_response");
409   if (get_configs_response
410       && (config = entity_child (get_configs_response, "config")))
411     {
412       config_t new_config;
413       const char *usage_type_text;
414       char *created_name, *comment, *type, *import_name;
415       entity_t usage_type;
416       array_t *import_nvt_selectors, *import_preferences;
417       int all_selector;
418 
419       /* Allow user to overwrite usage type. */
420       usage_type = entity_child (entity, "usage_type");
421       if (usage_type && strcmp (entity_text (usage_type), ""))
422         usage_type_text = entity_text (usage_type);
423       else
424         {
425           usage_type = entity_child (config, "usage_type");
426           if (usage_type)
427             usage_type_text = entity_text (usage_type);
428           else
429             usage_type_text = NULL;
430         }
431 
432       /* Get the config data from the XML. */
433 
434       if (parse_config_entity (config, NULL, &import_name, &comment, &type,
435                                NULL, &all_selector, &import_nvt_selectors,
436                                &import_preferences))
437         {
438           SEND_TO_CLIENT_OR_FAIL
439            (XML_ERROR_SYNTAX ("create_config",
440                               "Error in PREFERENCES element."));
441           log_event_fail ("config", "Scan config", NULL, "created");
442 
443           /* Cleanup. */
444 
445           create_config_reset ();
446           return;
447         }
448 
449       /* Create config. */
450 
451       switch (create_config (NULL,                  /* Generate a UUID. */
452                              import_name,
453                              1,                     /* Make name unique. */
454                              comment,
455                              all_selector,
456                              import_nvt_selectors,
457                              import_preferences,
458                              type,
459                              usage_type_text,
460                              &new_config,
461                              &created_name))
462         {
463           case 0:
464             {
465               gchar *uuid = config_uuid (new_config);
466               SENDF_TO_CLIENT_OR_FAIL
467                ("<create_config_response"
468                 " status=\"" STATUS_OK_CREATED "\""
469                 " status_text=\"" STATUS_OK_CREATED_TEXT "\""
470                 " id=\"%s\">"
471                 /* This is a hack for the GSA, which should really
472                  * do a GET_CONFIG with the ID to get the name. */
473                 "<config id=\"%s\"><name>%s</name></config>"
474                 "</create_config_response>",
475                 uuid,
476                 uuid,
477                 created_name);
478               log_event ("config", "Scan config", uuid, "created");
479               g_free (uuid);
480               free (created_name);
481               break;
482             }
483           case 1:
484             SEND_TO_CLIENT_OR_FAIL
485              (XML_ERROR_SYNTAX ("create_config",
486                                 "Config exists already"));
487             log_event_fail ("config", "Scan config", NULL, "created");
488             break;
489           case 99:
490             SEND_TO_CLIENT_OR_FAIL
491              (XML_ERROR_SYNTAX ("create_config",
492                                 "Permission denied"));
493             log_event_fail ("config", "Scan config", NULL, "created");
494             break;
495           case -1:
496             SEND_TO_CLIENT_OR_FAIL
497              (XML_INTERNAL_ERROR ("create_config"));
498             log_event_fail ("config", "Scan config", NULL, "created");
499             break;
500           case -2:
501             SEND_TO_CLIENT_OR_FAIL
502              (XML_ERROR_SYNTAX ("create_config",
503                                 "Import name must be at"
504                                 " least one character long"));
505             log_event_fail ("config", "Scan config", NULL, "created");
506             break;
507           case -3:
508             SEND_TO_CLIENT_OR_FAIL
509              (XML_ERROR_SYNTAX ("create_config",
510                                 "Error in NVT_SELECTORS element."));
511             log_event_fail ("config", "Scan config", NULL, "created");
512             break;
513           case -4:
514             SEND_TO_CLIENT_OR_FAIL
515              (XML_ERROR_SYNTAX ("create_config",
516                                 "Error in PREFERENCES element."));
517             log_event_fail ("config", "Scan config", NULL, "created");
518             break;
519         }
520 
521       /* Cleanup. */
522 
523       cleanup_import_preferences (import_preferences);
524       array_free (import_nvt_selectors);
525 
526       create_config_reset ();
527       return;
528     }
529 
530   /* Check for creation from scanner. */
531 
532   scanner = entity_child (entity, "scanner");
533   if (scanner && strlen (entity_text (scanner)))
534     {
535       char *uuid;
536 
537       uuid = NULL;
538 
539       switch (create_config_from_scanner
540                (entity_text (scanner),
541                 text_or_null (entity_child (entity, "name")),
542                 text_or_null (entity_child (entity, "comment")),
543                 text_or_null (entity_child (entity, "usage_type")),
544                 &uuid))
545         {
546           case 0:
547             SENDF_TO_CLIENT_OR_FAIL (XML_OK_CREATED_ID
548                                       ("create_config"), uuid);
549             log_event ("config", "Scan config", uuid, "created");
550             break;
551           case 1:
552             SENDF_TO_CLIENT_OR_FAIL
553              (XML_ERROR_SYNTAX ("create_config",
554                                 "Failed to find scanner"));
555             break;
556           case 2:
557             SENDF_TO_CLIENT_OR_FAIL
558              (XML_ERROR_SYNTAX ("create_config",
559                                 "Scanner not of type OSP"));
560             break;
561           case 3:
562             SENDF_TO_CLIENT_OR_FAIL
563              (XML_ERROR_SYNTAX ("create_config",
564                                 "Config name exists already"));
565             break;
566           case 4:
567             SENDF_TO_CLIENT_OR_FAIL
568              (XML_ERROR_SYNTAX ("create_config",
569                                 "Failed to get params from scanner"
570                                 " - the scanner may be offline or not"
571                                 " configured correctly"));
572             break;
573           case 99:
574             SEND_TO_CLIENT_OR_FAIL
575              (XML_ERROR_SYNTAX ("create_config",
576                                 "Permission denied"));
577             log_event_fail ("config", "Scan config", NULL, "created");
578             break;
579           case -1:
580           default:
581             SEND_TO_CLIENT_OR_FAIL
582              (XML_INTERNAL_ERROR ("create_config"));
583             log_event_fail ("config", "Scan config", NULL, "created");
584             break;
585         }
586       g_free (uuid);
587 
588       create_config_reset ();
589       return;
590     }
591 
592   /* Try copy from an existing config. */
593 
594   copy = entity_child (entity, "copy");
595   name = entity_child (entity, "name");
596 
597   if (((name == NULL) || (strlen (entity_text (name)) == 0))
598       && ((copy == NULL) || (strlen (entity_text (copy)) == 0)))
599     {
600       log_event_fail ("config", "Scan config", NULL, "created");
601       SEND_TO_CLIENT_OR_FAIL
602        (XML_ERROR_SYNTAX ("create_config",
603                           "Name and base config to copy"
604                           " must be at least one character long"));
605     }
606   else if (copy == NULL)
607     {
608       log_event_fail ("config", "Scan config", NULL, "created");
609       SEND_TO_CLIENT_OR_FAIL
610        (XML_ERROR_SYNTAX ("create_config",
611                           "A COPY element is required"));
612     }
613   else
614     {
615       config_t new_config;
616       entity_t comment, usage_type;
617 
618       comment = entity_child (entity, "comment");
619       usage_type = entity_child (entity, "usage_type");
620 
621       switch (copy_config (entity_text (name),
622                            comment ? entity_text (comment) : "",
623                            entity_text (copy),
624                            usage_type ? entity_text (usage_type) : NULL,
625                            &new_config))
626         {
627           case 0:
628             {
629               char *uuid = config_uuid (new_config);
630               SENDF_TO_CLIENT_OR_FAIL (XML_OK_CREATED_ID ("create_config"),
631                                        uuid);
632               log_event ("config", "Scan config", uuid, "created");
633               free (uuid);
634               break;
635             }
636           case 1:
637             SEND_TO_CLIENT_OR_FAIL
638              (XML_ERROR_SYNTAX ("create_config",
639                                 "Config exists already"));
640             log_event_fail ("config", "Scan config", NULL, "created");
641             break;
642           case 2:
643             if (send_find_error_to_client ("create_config", "config",
644                                            entity_text (copy),
645                                            gmp_parser))
646               {
647                 error_send_to_client (error);
648                 return;
649               }
650             log_event_fail ("config", "Config", NULL, "created");
651             break;
652           case 99:
653             SEND_TO_CLIENT_OR_FAIL
654              (XML_ERROR_SYNTAX ("create_config",
655                                 "Permission denied"));
656             log_event_fail ("config", "Scan config", NULL, "created");
657             break;
658           case -1:
659           default:
660             SEND_TO_CLIENT_OR_FAIL
661              (XML_INTERNAL_ERROR ("create_config"));
662             log_event_fail ("config", "Scan config", NULL, "created");
663             break;
664         }
665     }
666 
667   create_config_reset ();
668 }
669 
670 /**
671  * @brief End element.
672  *
673  * @param[in]  gmp_parser   GMP parser.
674  * @param[in]  error        Error parameter.
675  * @param[in]  name         Element name.
676  *
677  * @return 0 success, 1 command finished.
678  */
679 int
create_config_element_end(gmp_parser_t * gmp_parser,GError ** error,const gchar * name)680 create_config_element_end (gmp_parser_t *gmp_parser, GError **error,
681                            const gchar *name)
682 {
683   xml_handle_end_element (create_config_data.context, name);
684   if (create_config_data.context->done)
685     {
686       create_config_run (gmp_parser, error);
687       return 1;
688     }
689   return 0;
690 }
691 
692 /**
693  * @brief Add text to element.
694  *
695  * @param[in]  text         Text.
696  * @param[in]  text_len     Text length.
697  */
698 void
create_config_element_text(const gchar * text,gsize text_len)699 create_config_element_text (const gchar *text, gsize text_len)
700 {
701   xml_handle_text (create_config_data.context, text, text_len);
702 }
703 
704 
705 /* MODIFY_CONFIG. */
706 
707 /**
708  * @brief The modify_config command.
709  */
710 typedef struct
711 {
712   context_data_t *context;     ///< XML parser context.
713 } modify_config_t;
714 
715 /**
716  * @brief Parser callback data.
717  *
718  * This is initially 0 because it's a global variable.
719  */
720 static modify_config_t modify_config_data;
721 
722 /**
723  * @brief Reset command data.
724  */
725 static void
modify_config_reset()726 modify_config_reset ()
727 {
728   if (modify_config_data.context->first)
729     {
730       free_entity (modify_config_data.context->first->data);
731       g_slist_free_1 (modify_config_data.context->first);
732     }
733   g_free (modify_config_data.context);
734   memset (&modify_config_data, 0, sizeof (modify_config_t));
735 }
736 
737 /**
738  * @brief Start a command.
739  *
740  * @param[in]  gmp_parser        GMP parser.
741  * @param[in]  attribute_names   All attribute names.
742  * @param[in]  attribute_values  All attribute values.
743  */
744 void
modify_config_start(gmp_parser_t * gmp_parser,const gchar ** attribute_names,const gchar ** attribute_values)745 modify_config_start (gmp_parser_t *gmp_parser,
746                      const gchar **attribute_names,
747                      const gchar **attribute_values)
748 {
749   memset (&modify_config_data, 0, sizeof (modify_config_t));
750   modify_config_data.context = g_malloc0 (sizeof (context_data_t));
751   modify_config_element_start (gmp_parser, "modify_config", attribute_names,
752                                attribute_values);
753 }
754 
755 /**
756  * @brief Start element.
757  *
758  * @param[in]  gmp_parser        GMP parser.
759  * @param[in]  name              Element name.
760  * @param[in]  attribute_names   All attribute names.
761  * @param[in]  attribute_values  All attribute values.
762  */
763 void
modify_config_element_start(gmp_parser_t * gmp_parser,const gchar * name,const gchar ** attribute_names,const gchar ** attribute_values)764 modify_config_element_start (gmp_parser_t *gmp_parser, const gchar *name,
765                              const gchar **attribute_names,
766                              const gchar **attribute_values)
767 {
768   xml_handle_start_element (modify_config_data.context, name, attribute_names,
769                             attribute_values);
770 }
771 
772 /**
773  * @brief Handle basic, single-value fields of modify_config.
774  *
775  * @param[in]  config       The config to modify.
776  * @param[in]  name         The name to set or NULL to keep old value.
777  * @param[in]  comment      The comment to set or NULL to keep old value.
778  * @param[in]  scanner_id   The scanner ID to set or NULL to keep old value.
779  * @param[in]  gmp_parser   GMP parser.
780  * @param[out] error        GError output.
781  *
782  * @return 0 on success, -1 on error.
783  */
784 static int
modify_config_handle_basic_fields(config_t config,const char * name,const char * comment,const char * scanner_id,gmp_parser_t * gmp_parser,GError ** error)785 modify_config_handle_basic_fields (config_t config,
786                                    const char *name,
787                                    const char *comment,
788                                    const char *scanner_id,
789                                    gmp_parser_t *gmp_parser,
790                                    GError **error)
791 {
792   switch (manage_set_config (config, name, comment, scanner_id))
793     {
794       case 0:
795         return 0;
796       case 1:
797         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
798           (-1, XML_ERROR_SYNTAX ("modify_config", "Name must be unique"));
799         return -1;
800       case 2:
801         if (send_find_error_to_client ("modify_config",
802                                        "scanner",
803                                        scanner_id,
804                                        gmp_parser))
805           {
806             error_send_to_client (error);
807             return -1;
808           }
809         return -1;
810       case 3:
811         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
812           (-1, XML_ERROR_SYNTAX ("modify_config", "Config is in use"));
813         return -1;
814       case -1:
815         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
816           (-1, XML_INTERNAL_ERROR ("modify_config"));
817         return -1;
818       default:
819         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
820           (-1, XML_INTERNAL_ERROR ("modify_config"));
821         return -1;
822     }
823 }
824 
825 /**
826  * @brief Collect VT families from parsed modify_config XML into arrays.
827  *
828  * Family name strings are to be freed with entity.
829  * VT families not collected are assumed to be static and empty.
830  *
831  * @param[in]  entities   The entities struct with family elems as children.
832  * @param[out] families_growing_all    Array of growing families with all VTs.
833  * @param[out] families_growing_empty  Array of growing, empty families.
834  * @param[out] families_static_all     Array of static families with all VTs.
835  *
836  * @return 0 on success, -1 on error.
837  */
838 static int
modify_config_collect_selection_families(entities_t entities,array_t ** families_growing_all,array_t ** families_growing_empty,array_t ** families_static_all)839 modify_config_collect_selection_families (entities_t entities,
840                                           array_t **families_growing_all,
841                                           array_t **families_growing_empty,
842                                           array_t **families_static_all)
843 {
844   entities_t iter_entities;
845   entity_t entity;
846 
847   assert (families_growing_all);
848   assert (families_growing_empty);
849   assert (families_static_all);
850 
851   *families_growing_all = make_array ();
852   *families_growing_empty = make_array ();
853   *families_static_all = make_array ();
854 
855   iter_entities = entities;
856   while ((entity = first_entity (iter_entities)))
857     {
858       if (strcmp (entity_name (entity), "family") == 0)
859         {
860           char *name, *growing_str, *all_str;
861           name = text_or_null (entity_child (entity, "name"));
862           all_str = text_or_null (entity_child (entity, "all"));
863           growing_str = text_or_null (entity_child (entity, "growing"));
864 
865           if (growing_str && strcmp (growing_str, "0"))
866             {
867               if (all_str && strcmp (all_str, "0"))
868                 array_add (*families_growing_all, name);
869               else
870                 array_add (*families_growing_empty, name);
871             }
872           else if (all_str && strcmp (all_str, "0"))
873             array_add (*families_static_all, name);
874         }
875       iter_entities = next_entities (iter_entities);
876     }
877 
878   array_terminate (*families_growing_all);
879   array_terminate (*families_growing_empty);
880   array_terminate (*families_static_all);
881 
882   return 0;
883 }
884 
885 /**
886  * @brief Handles a family selection inside a modify_config command.
887  *
888  * @param[in]  config                   The config to modify.
889  * @param[in]  families_growing_all     Array of growing families with all VTs.
890  * @param[in]  families_growing_empty   Array of growing, empty families.
891  * @param[in]  families_static_all      Array of static families with all VTs.
892  * @param[in]  family_selection_growing 1 if families should grow, else 0.
893  * @param[in]  gmp_parser               The GMP parser.
894  * @param[out] error                    GError output.
895  *
896  * @return 0 on success, -1 on error.
897  */
898 static int
modify_config_handle_family_selection(config_t config,array_t * families_growing_all,array_t * families_growing_empty,array_t * families_static_all,int family_selection_growing,gmp_parser_t * gmp_parser,GError ** error)899 modify_config_handle_family_selection (config_t config,
900                                        array_t *families_growing_all,
901                                        array_t *families_growing_empty,
902                                        array_t *families_static_all,
903                                        int family_selection_growing,
904                                        gmp_parser_t *gmp_parser,
905                                        GError **error)
906 {
907   gchar *rejected_family;
908 
909   switch (manage_set_config_families
910              (config,
911               families_growing_all,
912               families_static_all,
913               families_growing_empty,
914               family_selection_growing,
915               &rejected_family))
916     {
917       case 0:
918         return 0;
919       case 1:
920         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
921           (-1, XML_ERROR_SYNTAX ("modify_config", "Config is in use"));
922         return -1;
923       case 2:
924         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
925           (-1,
926            XML_ERROR_SYNTAX ("modify_config",
927                              "Family &quot;%s&quot; must be growing and"
928                              " include all VTs or it must be static and"
929                              " empty."),
930            rejected_family);
931         g_free (rejected_family);
932         return -1;
933       case -1:
934         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
935           (-1, XML_INTERNAL_ERROR ("modify_config"));
936         return -1;
937       default:
938         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
939           (-1, XML_INTERNAL_ERROR ("modify_config"));
940         return -1;
941     }
942 }
943 
944 /**
945  * @brief Collect a list of VT OIDs for a particular family in modify_config.
946  *
947  * @param[in]  entities  The entities containing nvt elements as children.
948  * @param[out] nvt_oids  The list of VT OIDs to select.
949  *
950  * @return 0 on success, -1 on error.
951  */
952 static int
modify_config_collect_selection_nvts(entities_t entities,array_t ** nvt_oids)953 modify_config_collect_selection_nvts (entities_t entities,
954                                       array_t **nvt_oids)
955 {
956   entities_t iter_entities;
957   entity_t entity;
958 
959   assert (entities);
960   assert (nvt_oids);
961 
962   *nvt_oids = make_array ();
963 
964   iter_entities = entities;
965   while ((entity = first_entity (iter_entities)))
966     {
967       if (strcmp (entity_name (entity), "nvt") == 0)
968         {
969           char *oid;
970           oid = attr_or_null (entity, "oid");
971           if (oid)
972             array_add (*nvt_oids, oid);
973         }
974       iter_entities = next_entities (iter_entities);
975     }
976 
977   array_terminate (*nvt_oids);
978 
979   return 0;
980 }
981 
982 /**
983  * @brief Changes the VT selection of a given family in modify_config.
984  *
985  * @param[in]  config               The config to modify.
986  * @param[in]  nvt_selection_family The family to set the VT selection of.
987  * @param[in]  nvt_selection        Array of VT OIDs to select of the family.
988  * @param[in]  gmp_parser           The GMP parser.
989  * @param[out] error                GError output.
990  *
991  * @return 0 on success, -1 on error.
992  */
993 static int
modify_config_handle_nvt_selection(config_t config,const char * nvt_selection_family,GPtrArray * nvt_selection,gmp_parser_t * gmp_parser,GError ** error)994 modify_config_handle_nvt_selection (config_t config,
995                                     const char *nvt_selection_family,
996                                     GPtrArray *nvt_selection,
997                                     gmp_parser_t *gmp_parser,
998                                     GError **error)
999 {
1000   switch (manage_set_config_nvts
1001             (config,
1002              nvt_selection_family,
1003              nvt_selection))
1004     {
1005       case 0:
1006         return 0;
1007       case 1:
1008         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
1009           (-1, XML_ERROR_SYNTAX ("modify_config", "Config is in use"));
1010         return -1;
1011       case 2:
1012         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
1013           (-1,
1014            XML_ERROR_SYNTAX ("modify_config",
1015                              "Attempt to modify NVT in whole-only family %s"),
1016            nvt_selection_family);
1017         return -1;
1018       case -1:
1019         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
1020           (-1, XML_INTERNAL_ERROR ("modify_config"));
1021         return -1;
1022       default:
1023         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
1024           (-1, XML_INTERNAL_ERROR ("modify_config"));
1025         return -1;
1026     }
1027 }
1028 
1029 /**
1030  * @brief Modifies a single preference inside a modify_config command.
1031  *
1032  * @param[in]  config     The config to modify
1033  * @param[in]  nvt_oid    VT OID of the preference or NULL for scanner pref.
1034  * @param[in]  name       Name of the preference to Changes
1035  * @param[in]  value      Value to set for the preference.
1036  * @param[in]  gmp_parser The GMP parser.
1037  * @param[out] error      GError output.
1038  *
1039  * @return 0 on success, -1 on error.
1040  */
1041 static int
modify_config_handle_preference(config_t config,const char * nvt_oid,const char * name,const char * value,gmp_parser_t * gmp_parser,GError ** error)1042 modify_config_handle_preference (config_t config,
1043                                  const char *nvt_oid,
1044                                  const char *name,
1045                                  const char *value,
1046                                  gmp_parser_t *gmp_parser,
1047                                  GError **error)
1048 {
1049   switch (manage_set_config_preference (config, nvt_oid, name, value))
1050     {
1051       case 0:
1052         return 0;
1053       case 1:
1054         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
1055           (-1, XML_ERROR_SYNTAX ("modify_config", "Config is in use"));
1056         return -1;
1057       case 2:
1058         if (nvt_oid)
1059           {
1060             SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
1061               (-1,
1062                XML_ERROR_SYNTAX ("modify_config",
1063                                  "Empty radio value for preference %s"
1064                                  " of NVT %s"),
1065                name,
1066                nvt_oid);
1067           }
1068         else
1069           {
1070             SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
1071               (-1,
1072                XML_ERROR_SYNTAX ("modify_config",
1073                                  "Empty radio value for preference %s"),
1074                nvt_oid);
1075           }
1076         return -1;
1077       case -1:
1078         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
1079           (-1, XML_INTERNAL_ERROR ("modify_config"));
1080         return -1;
1081       default:
1082         SENDF_TO_CLIENT_OR_FAIL_WITH_RETURN
1083           (-1, XML_INTERNAL_ERROR ("modify_config"));
1084         return -1;
1085     }
1086 }
1087 
1088 /**
1089  * @brief Execute command.
1090  *
1091  * @param[in]  gmp_parser   GMP parser.
1092  * @param[out] error        Error parameter.
1093  */
1094 static void
modify_config_run(gmp_parser_t * gmp_parser,GError ** error)1095 modify_config_run (gmp_parser_t *gmp_parser, GError **error)
1096 {
1097   entity_t entity, child;
1098   entities_t children;
1099   const char *config_id;
1100   config_t config;
1101 
1102   entity = (entity_t) modify_config_data.context->first->data;
1103 
1104   // Check command permission
1105   if (acl_user_may ("modify_config") == 0)
1106     {
1107       SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ("modify_config",
1108                                                 "Permission denied"));
1109       return;
1110     }
1111 
1112   config_id = attr_or_null (entity, "config_id");
1113 
1114   if (config_id == NULL)
1115     SEND_TO_CLIENT_OR_FAIL
1116      (XML_ERROR_SYNTAX ("modify_config",
1117                         "A config_id attribute is required"));
1118   else if (config_predefined_uuid (config_id))
1119     SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ("modify_config",
1120                                               "Permission denied"));
1121 
1122   // Find the config
1123   switch (manage_modify_config_start (config_id, &config))
1124     {
1125       case 0:
1126         break;
1127       case 1:
1128         if (send_find_error_to_client ("modify_config",
1129                                        "config",
1130                                        config_id,
1131                                        gmp_parser))
1132           {
1133             error_send_to_client (error);
1134             return;
1135           }
1136         log_event_fail ("config", "Scan Config", config_id, "modified");
1137         return;
1138       default:
1139         SEND_TO_CLIENT_OR_FAIL
1140           (XML_INTERNAL_ERROR ("modify_config"));
1141         log_event_fail ("config", "Scan Config", config_id, "modified");
1142     }
1143 
1144   // Handle basic attributes and elements
1145   if (modify_config_handle_basic_fields
1146          (config,
1147           text_or_null (entity_child (entity, "name")),
1148           text_or_null (entity_child (entity, "comment")),
1149           text_or_null (entity_child (entity, "scanner")),
1150           gmp_parser,
1151           error))
1152     {
1153       manage_modify_config_cancel ();
1154       log_event_fail ("config", "Scan config", config_id, "modified");
1155       return;
1156     }
1157 
1158   // Preferences and NVT selections
1159   children = entity->entities;
1160   while ((child = first_entity (children)))
1161     {
1162       if (strcmp (child->name, "family_selection") == 0)
1163         {
1164           array_t *families_growing_all;
1165           array_t *families_growing_empty;
1166           array_t *families_static_all;
1167           const char *growing_str;
1168           int growing;
1169 
1170           growing_str = text_or_null (entity_child (child, "growing"));
1171           growing = (growing_str && strcmp (growing_str, "0")) ? 1 : 0;
1172 
1173           if (modify_config_collect_selection_families
1174                  (child->entities,
1175                   &families_growing_all,
1176                   &families_growing_empty,
1177                   &families_static_all)
1178               || modify_config_handle_family_selection
1179                    (config,
1180                     families_growing_all,
1181                     families_growing_empty,
1182                     families_static_all,
1183                     growing,
1184                     gmp_parser,
1185                     error))
1186             {
1187               manage_modify_config_cancel ();
1188               log_event_fail ("config", "Scan config", config_id, "modified");
1189               g_ptr_array_free (families_growing_all, TRUE);
1190               g_ptr_array_free (families_growing_empty, TRUE);
1191               g_ptr_array_free (families_static_all, TRUE);
1192               return;
1193             }
1194           g_ptr_array_free (families_growing_all, TRUE);
1195           g_ptr_array_free (families_growing_empty, TRUE);
1196           g_ptr_array_free (families_static_all, TRUE);
1197         }
1198       else if (strcmp (child->name, "nvt_selection") == 0)
1199         {
1200           array_t *nvt_selection;
1201 
1202           if (modify_config_collect_selection_nvts
1203                  (child->entities,
1204                   &nvt_selection)
1205               || modify_config_handle_nvt_selection
1206                    (config,
1207                     text_or_null (entity_child (child, "family")),
1208                     nvt_selection,
1209                     gmp_parser,
1210                     error))
1211             {
1212               manage_modify_config_cancel ();
1213               log_event_fail ("config", "Scan config", config_id, "modified");
1214               g_ptr_array_free (nvt_selection, TRUE);
1215               return;
1216             }
1217           g_ptr_array_free (nvt_selection, TRUE);
1218         }
1219       else if (strcmp (child->name, "preference") == 0)
1220         {
1221           if (modify_config_handle_preference
1222                  (config,
1223                   attr_or_null (entity_child (child, "nvt"), "oid"),
1224                   text_or_null (entity_child (child, "name")),
1225                   text_or_null (entity_child (child, "value")),
1226                   gmp_parser,
1227                   error))
1228             {
1229               manage_modify_config_cancel ();
1230               log_event_fail ("config", "Scan config", config_id, "modified");
1231               return;
1232             }
1233         }
1234       children = next_entities (children);
1235     }
1236 
1237   manage_modify_config_commit ();
1238   SEND_TO_CLIENT_OR_FAIL (XML_OK ("modify_config"));
1239   log_event ("config", "Scan Config", config_id, "modified");
1240 
1241   // Cleanup
1242   modify_config_reset ();
1243 }
1244 
1245 /**
1246  * @brief End element.
1247  *
1248  * @param[in]  gmp_parser   GMP parser.
1249  * @param[in]  error        Error parameter.
1250  * @param[in]  name         Element name.
1251  *
1252  * @return 0 success, 1 command finished.
1253  */
1254 int
modify_config_element_end(gmp_parser_t * gmp_parser,GError ** error,const gchar * name)1255 modify_config_element_end (gmp_parser_t *gmp_parser, GError **error,
1256                            const gchar *name)
1257 {
1258   xml_handle_end_element (modify_config_data.context, name);
1259   if (modify_config_data.context->done)
1260     {
1261       modify_config_run (gmp_parser, error);
1262       return 1;
1263     }
1264   return 0;
1265 }
1266 
1267 /**
1268  * @brief Add text to element.
1269  *
1270  * @param[in]  text         Text.
1271  * @param[in]  text_len     Text length.
1272  */
1273 void
modify_config_element_text(const gchar * text,gsize text_len)1274 modify_config_element_text (const gchar *text, gsize text_len)
1275 {
1276   xml_handle_text (modify_config_data.context, text, text_len);
1277 }
1278