1 /* Copyright (C) 2013-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  manage_acl.c
21  * @brief The Greenbone Vulnerability Manager management library (Access
22  * Control Layer).
23  *
24  * This file isolates the access control portions of the GVM
25  * management library.
26  */
27 
28 #include "manage_acl.h"
29 #include "manage_sql.h"
30 #include "sql.h"
31 
32 #include <assert.h>
33 #include <stdlib.h>
34 #include <string.h>
35 
36 #undef G_LOG_DOMAIN
37 /**
38  * @brief GLib log domain.
39  */
40 #define G_LOG_DOMAIN "md manage"
41 
42 /**
43  * @brief Test whether the current user may perform an operation.
44  *
45  * Does not check if the user is special.
46  *
47  * @param[in]  operation  Name of operation.
48  *
49  * @return 1 if user has permission, else 0.
50  */
51 static int
user_may_internal(const char * operation)52 user_may_internal (const char *operation)
53 {
54   int ret;
55   gchar *quoted_operation;
56 
57   assert (operation);
58 
59   quoted_operation = sql_quote (operation);
60 
61   ret = sql_int (ACL_USER_MAY ("0"),
62                  current_credentials.uuid,
63                  current_credentials.uuid,
64                  current_credentials.uuid,
65                  quoted_operation,
66                  quoted_operation,
67                  quoted_operation,
68                  quoted_operation);
69 
70   g_free (quoted_operation);
71 
72   return ret;
73 }
74 
75 /**
76  * @brief Check if a string array contains a string, ignoring case.
77  *
78  * @param[in]  strv    String array.
79  * @param[in]  string  String.
80  *
81  * @return 1 if strv contains string, else 0.
82  */
83 static int
strv_case_eq(gchar ** strv,const gchar * string)84 strv_case_eq (gchar **strv, const gchar *string)
85 {
86   if (string == NULL)
87     return 0;
88 
89   while (*strv)
90     if (strcasecmp (*strv, string) == 0)
91       return 1;
92     else
93       strv++;
94 
95   return 0;
96 }
97 
98 /**
99  * @brief Get commands that the current user may run.
100  *
101  * @param[in]  disabled_commands  All disabled commands.
102  *
103  * @return Freshly allocated list of commands.  Free with g_free.
104  */
105 command_t *
acl_commands(gchar ** disabled_commands)106 acl_commands (gchar **disabled_commands)
107 {
108   command_t *all, *commands;
109   int index, length, special_user;
110 
111   /* Count maximum number of commands. */
112 
113   length = 1;
114   all = gmp_commands;
115   while ((*all).name)
116     {
117       length++;
118       all++;
119     }
120 
121   /* Fill return array with allowed commands. */
122 
123   special_user = ((current_credentials.uuid == NULL)
124                   || (strlen (current_credentials.uuid) == 0));
125 
126   if (special_user == 0)
127     special_user = sql_int ("SELECT user_can_everything ('%s');",
128                             current_credentials.uuid);
129 
130   commands = g_malloc0 (length * sizeof (*commands));
131   all = gmp_commands;
132   index = 0;
133   while ((*all).name)
134     {
135       if ((disabled_commands == NULL
136            || strv_case_eq (disabled_commands, (*all).name)
137               == 0)
138           && (special_user
139               || user_may_internal ((*all).name)))
140         {
141           commands[index].name = (*all).name;
142           commands[index].summary = (*all).summary;
143           index++;
144         }
145       all++;
146     }
147 
148   return commands;
149 }
150 
151 /**
152  * @brief Test whether a user may perform an operation.
153  *
154  * @param[in]  operation  Name of operation.
155  *
156  * @return 1 if user has permission, else 0.
157  */
158 int
acl_user_may(const char * operation)159 acl_user_may (const char *operation)
160 {
161   if (strlen (current_credentials.uuid) == 0)
162     /* Allow the dummy user in init_manage to do anything. */
163     return 1;
164 
165   if (sql_int ("SELECT user_can_everything ('%s');",
166                current_credentials.uuid))
167     return 1;
168 
169   return user_may_internal (operation);
170 }
171 
172 /**
173  * @brief Check whether a role has Super Admin capability.
174  *
175  * @param[in]  role_id  ID of role.
176  *
177  * @return 1 if role can Super Admin, else 0.
178  */
179 int
acl_role_can_super_everyone(const char * role_id)180 acl_role_can_super_everyone (const char *role_id)
181 {
182   gchar *quoted_role_id;
183   quoted_role_id = sql_quote (role_id);
184   if (sql_int (" SELECT EXISTS (SELECT * FROM permissions"
185                "                WHERE name = 'Super'"
186                /*                    Super on everyone. */
187                "                AND (resource = 0)"
188                "                AND subject_location"
189                "                    = " G_STRINGIFY (LOCATION_TABLE)
190                "                AND (subject_type = 'role'"
191                "                     AND subject"
192                "                         = (SELECT id"
193                "                            FROM roles"
194                "                            WHERE uuid = '%s')));",
195                role_id))
196     {
197       g_free (quoted_role_id);
198       return 1;
199     }
200   g_free (quoted_role_id);
201   return 0;
202 }
203 
204 /**
205  * @brief Check whether a user is a Super Admin.
206  *
207  * @param[in]  uuid  Uuid of user.
208  *
209  * @return 1 if user is a Super Admin, else 0.
210  */
211 int
acl_user_can_super_everyone(const char * uuid)212 acl_user_can_super_everyone (const char *uuid)
213 {
214   gchar *quoted_uuid;
215 
216   quoted_uuid = sql_quote (uuid);
217   if (sql_int (" SELECT EXISTS (SELECT * FROM permissions"
218                "                WHERE name = 'Super'"
219                /*                    Super on everyone. */
220                "                AND (resource = 0)"
221                "                AND subject_location"
222                "                    = " G_STRINGIFY (LOCATION_TABLE)
223                "                AND ((subject_type = 'user'"
224                "                      AND subject"
225                "                          = (SELECT id FROM users"
226                "                             WHERE users.uuid = '%s'))"
227                "                     OR (subject_type = 'group'"
228                "                         AND subject"
229                "                             IN (SELECT DISTINCT \"group\""
230                "                                 FROM group_users"
231                "                                 WHERE \"user\""
232                "                                       = (SELECT id"
233                "                                          FROM users"
234                "                                          WHERE users.uuid"
235                "                                                = '%s')))"
236                "                     OR (subject_type = 'role'"
237                "                         AND subject"
238                "                             IN (SELECT DISTINCT role"
239                "                                 FROM role_users"
240                "                                 WHERE \"user\""
241                "                                       = (SELECT id"
242                "                                          FROM users"
243                "                                          WHERE users.uuid"
244                "                                                = '%s')))));",
245                quoted_uuid,
246                quoted_uuid,
247                quoted_uuid))
248     {
249       g_free (quoted_uuid);
250       return 1;
251     }
252   g_free (quoted_uuid);
253   return 0;
254 }
255 
256 /**
257  * @brief Test whether a user may perform any operation.
258  *
259  * @param[in]  user_id  UUID of user.
260  *
261  * @return 1 if user has permission, else 0.
262  */
263 int
acl_user_can_everything(const char * user_id)264 acl_user_can_everything (const char *user_id)
265 {
266   gchar *quoted_user_id;
267   int ret;
268 
269   quoted_user_id = sql_quote (user_id);
270   ret = sql_int ("SELECT count(*) > 0 FROM permissions"
271                  " WHERE resource = 0"
272                  " AND subject_location"
273                  "     = " G_STRINGIFY (LOCATION_TABLE)
274                  " AND ((subject_type = 'user'"
275                  "       AND subject"
276                  "           = (SELECT id FROM users"
277                  "              WHERE users.uuid = '%s'))"
278                  "      OR (subject_type = 'group'"
279                  "          AND subject"
280                  "              IN (SELECT DISTINCT \"group\""
281                  "                  FROM group_users"
282                  "                  WHERE \"user\" = (SELECT id"
283                  "                                    FROM users"
284                  "                                    WHERE users.uuid"
285                  "                                          = '%s')))"
286                  "      OR (subject_type = 'role'"
287                  "          AND subject"
288                  "              IN (SELECT DISTINCT role"
289                  "                  FROM role_users"
290                  "                  WHERE \"user\" = (SELECT id"
291                  "                                    FROM users"
292                  "                                    WHERE users.uuid"
293                  "                                          = '%s'))))"
294                  " AND name = 'Everything';",
295                  quoted_user_id,
296                  quoted_user_id,
297                  quoted_user_id);
298   g_free (quoted_user_id);
299   return ret;
300 }
301 
302 /**
303  * @brief Test whether a user has super permission on another user.
304  *
305  * @param[in]  super_user_id  UUID of user who may have super permission.
306  * @param[in]  other_user     Other user.
307  *
308  * @return 1 if user has permission, else 0.
309  */
310 int
acl_user_has_super(const char * super_user_id,user_t other_user)311 acl_user_has_super (const char *super_user_id, user_t other_user)
312 {
313   gchar *quoted_super_user_id;
314 
315   quoted_super_user_id = sql_quote (super_user_id);
316   if (sql_int (" SELECT EXISTS (SELECT * FROM permissions"
317                "                WHERE name = 'Super'"
318                /*                    Super on everyone. */
319                "                AND ((resource = 0)"
320                /*                    Super on other_user. */
321                "                     OR ((resource_type = 'user')"
322                "                         AND (resource = %llu))"
323                /*                    Super on other_user's role. */
324                "                     OR ((resource_type = 'role')"
325                "                         AND (resource"
326                "                              IN (SELECT DISTINCT role"
327                "                                  FROM role_users"
328                "                                  WHERE \"user\" = %llu)))"
329                /*                    Super on other_user's group. */
330                "                     OR ((resource_type = 'group')"
331                "                         AND (resource"
332                "                              IN (SELECT DISTINCT \"group\""
333                "                                  FROM group_users"
334                "                                  WHERE \"user\" = %llu))))"
335                "                AND subject_location"
336                "                    = " G_STRINGIFY (LOCATION_TABLE)
337                "                AND ((subject_type = 'user'"
338                "                      AND subject"
339                "                          = (SELECT id FROM users"
340                "                             WHERE users.uuid = '%s'))"
341                "                     OR (subject_type = 'group'"
342                "                         AND subject"
343                "                             IN (SELECT DISTINCT \"group\""
344                "                                 FROM group_users"
345                "                                 WHERE \"user\""
346                "                                       = (SELECT id"
347                "                                          FROM users"
348                "                                          WHERE users.uuid"
349                "                                                = '%s')))"
350                "                     OR (subject_type = 'role'"
351                "                         AND subject"
352                "                             IN (SELECT DISTINCT role"
353                "                                 FROM role_users"
354                "                                 WHERE \"user\""
355                "                                       = (SELECT id"
356                "                                          FROM users"
357                "                                          WHERE users.uuid"
358                "                                                = '%s')))));",
359                other_user,
360                other_user,
361                other_user,
362                super_user_id,
363                super_user_id,
364                super_user_id))
365     {
366       g_free (quoted_super_user_id);
367       return 1;
368     }
369   g_free (quoted_super_user_id);
370   return 0;
371 }
372 
373 /**
374  * @brief Check whether a user is an Admin.
375  *
376  * @param[in]  uuid  Uuid of user.
377  *
378  * @return 1 if user is an Admin, else 0.
379  */
380 int
acl_user_is_admin(const char * uuid)381 acl_user_is_admin (const char *uuid)
382 {
383   int ret;
384   gchar *quoted_uuid;
385 
386   quoted_uuid = sql_quote (uuid);
387   ret = sql_int ("SELECT count (*) FROM role_users"
388                  " WHERE role = (SELECT id FROM roles"
389                  "               WHERE uuid = '" ROLE_UUID_ADMIN "')"
390                  " AND \"user\" = (SELECT id FROM users WHERE uuid = '%s');",
391                  quoted_uuid);
392   g_free (quoted_uuid);
393   return ret;
394 }
395 
396 /**
397  * @brief Check whether a user is an Observer.
398  *
399  * @param[in]  uuid  Uuid of user.
400  *
401  * @return 1 if user is an Observer, else 0.
402  */
403 int
acl_user_is_observer(const char * uuid)404 acl_user_is_observer (const char *uuid)
405 {
406   int ret;
407   gchar *quoted_uuid;
408 
409   quoted_uuid = sql_quote (uuid);
410   ret = sql_int ("SELECT count (*) FROM role_users"
411                  " WHERE role = (SELECT id FROM roles"
412                  "               WHERE uuid = '" ROLE_UUID_OBSERVER "')"
413                  " AND \"user\" = (SELECT id FROM users WHERE uuid = '%s');",
414                  quoted_uuid);
415   g_free (quoted_uuid);
416   return ret;
417 }
418 
419 /**
420  * @brief Check whether a user is a Super Admin.
421  *
422  * @param[in]  uuid  Uuid of user.
423  *
424  * @return 1 if user is a Super Admin, else 0.
425  */
426 int
acl_user_is_super_admin(const char * uuid)427 acl_user_is_super_admin (const char *uuid)
428 {
429   int ret;
430   gchar *quoted_uuid;
431 
432   quoted_uuid = sql_quote (uuid);
433   ret = sql_int ("SELECT count (*) FROM role_users"
434                  " WHERE role = (SELECT id FROM roles"
435                  "               WHERE uuid = '" ROLE_UUID_SUPER_ADMIN "')"
436                  " AND \"user\" = (SELECT id FROM users WHERE uuid = '%s');",
437                  quoted_uuid);
438   g_free (quoted_uuid);
439   return ret;
440 }
441 
442 /**
443  * @brief Check whether a user has the User role.
444  *
445  * @param[in]  uuid  Uuid of user.
446  *
447  * @return 1 if user has the User role, else 0.
448  */
449 int
acl_user_is_user(const char * uuid)450 acl_user_is_user (const char *uuid)
451 {
452   int ret;
453   gchar *quoted_uuid;
454 
455   quoted_uuid = sql_quote (uuid);
456   ret = sql_int ("SELECT count (*) FROM role_users"
457                  " WHERE role = (SELECT id FROM roles"
458                  "               WHERE uuid = '" ROLE_UUID_USER "')"
459                  " AND \"user\" = (SELECT id FROM users WHERE uuid = '%s');",
460                  quoted_uuid);
461   g_free (quoted_uuid);
462   return ret;
463 }
464 
465 /* TODO This is only predicatable for unique fields like "id".  If the field
466  *      is "name" then "SELECT ... format" will choose arbitrarily between
467  *      the resources that have the same name. */
468 /**
469  * @brief Super clause.
470  *
471  * @param[in]  format  Value format specifier.
472  */
473 #define ACL_SUPER_CLAUSE(format)                                          \
474   "                name = 'Super'"                                        \
475   /*                    Super on everyone. */                             \
476   "                AND ((resource = 0)"                                   \
477   /*                    Super on other_user. */                           \
478   "                     OR ((resource_type = 'user')"                     \
479   "                         AND (resource = (SELECT %ss%s.owner"          \
480   "                                          FROM %ss%s"                  \
481   "                                          WHERE %s = " format ")))"    \
482   /*                    Super on other_user's role. */                    \
483   "                     OR ((resource_type = 'role')"                     \
484   "                         AND (resource"                                \
485   "                              IN (SELECT DISTINCT role"                \
486   "                                  FROM role_users"                     \
487   "                                  WHERE \"user\""                      \
488   "                                        = (SELECT %ss%s.owner"         \
489   "                                           FROM %ss%s"                 \
490   "                                           WHERE %s"                   \
491   "                                                 = " format "))))"     \
492   /*                    Super on other_user's group. */                   \
493   "                     OR ((resource_type = 'group')"                    \
494   "                         AND (resource"                                \
495   "                              IN (SELECT DISTINCT \"group\""           \
496   "                                  FROM group_users"                    \
497   "                                  WHERE \"user\""                      \
498   "                                        = (SELECT %ss%s.owner"         \
499   "                                           FROM %ss%s"                 \
500   "                                           WHERE %s = " format ")))))" \
501   "                AND subject_location = " G_STRINGIFY (LOCATION_TABLE)  \
502   "                AND ((subject_type = 'user'"                           \
503   "                      AND subject"                                     \
504   "                          = (SELECT id FROM users"                     \
505   "                             WHERE users.uuid = '%s'))"                \
506   "                     OR (subject_type = 'group'"                       \
507   "                         AND subject"                                  \
508   "                             IN (SELECT DISTINCT \"group\""            \
509   "                                 FROM group_users"                     \
510   "                                 WHERE \"user\""                       \
511   "                                       = (SELECT id"                   \
512   "                                          FROM users"                  \
513   "                                          WHERE users.uuid"            \
514   "                                                = '%s')))"             \
515   "                     OR (subject_type = 'role'"                        \
516   "                         AND subject"                                  \
517   "                             IN (SELECT DISTINCT role"                 \
518   "                                 FROM role_users"                      \
519   "                                 WHERE \"user\""                       \
520   "                                       = (SELECT id"                   \
521   "                                          FROM users"                  \
522   "                                          WHERE users.uuid"            \
523   "                                                = '%s'))))"
524 
525 /**
526  * @brief Super clause arguments.
527  *
528  * @param[in]  type     Type of resource.
529  * @param[in]  field    Field to compare.  Typically "uuid".
530  * @param[in]  value    Expected value of field.
531  * @param[in]  user_id  UUID of user.
532  * @param[in]  trash    Whether to search trash.
533  */
534 #define ACL_SUPER_CLAUSE_ARGS(type, field, value, user_id, trash) \
535   type,                                                       \
536   trash ? (strcasecmp (type, "task") ? "_trash" : "") : "",   \
537   type,                                                       \
538   trash ? (strcasecmp (type, "task") ? "_trash" : "") : "",   \
539   field,                                                      \
540   value,                                                      \
541   type,                                                       \
542   trash ? (strcasecmp (type, "task") ? "_trash" : "") : "",   \
543   type,                                                       \
544   trash ? (strcasecmp (type, "task") ? "_trash" : "") : "",   \
545   field,                                                      \
546   value,                                                      \
547   type,                                                       \
548   trash ? (strcasecmp (type, "task") ? "_trash" : "") : "",   \
549   type,                                                       \
550   trash ? (strcasecmp (type, "task") ? "_trash" : "") : "",   \
551   field,                                                      \
552   value,                                                      \
553   user_id,                                                    \
554   user_id,                                                    \
555   user_id
556 
557 /**
558  * @brief Test whether a user has Super permission on a resource.
559  *
560  * @param[in]  type      Type of resource.
561  * @param[in]  field     Field to compare with value.
562  * @param[in]  value     Identifier value of resource.
563  * @param[in]  trash     Whether resource is in trash.
564  *
565  * @return 1 if user has Super, else 0.
566  */
567 static int
acl_user_has_super_on(const char * type,const char * field,const char * value,int trash)568 acl_user_has_super_on (const char *type, const char *field, const char *value,
569                        int trash)
570 {
571   gchar *quoted_value;
572   quoted_value = sql_quote (value);
573   if (sql_int ("SELECT EXISTS (SELECT * FROM permissions"
574                "               WHERE " ACL_SUPER_CLAUSE ("'%s'") ");",
575                ACL_SUPER_CLAUSE_ARGS (type, field, quoted_value,
576                                       current_credentials.uuid, trash)))
577     {
578       g_free (quoted_value);
579       return 1;
580     }
581   g_free (quoted_value);
582   return 0;
583 }
584 
585 /**
586  * @brief Test whether a user has Super permission on a resource.
587  *
588  * @param[in]  type      Type of resource.
589  * @param[in]  field     Field to compare with resource.
590  * @param[in]  resource  Resource.
591  * @param[in]  trash     Whether resource is in trash.
592  *
593  * @return 1 if user has Super, else 0.
594  */
595 static int
acl_user_has_super_on_resource(const char * type,const char * field,resource_t resource,int trash)596 acl_user_has_super_on_resource (const char *type, const char *field,
597                                 resource_t resource, int trash)
598 {
599   if (sql_int ("SELECT EXISTS (SELECT * FROM permissions"
600                "               WHERE " ACL_SUPER_CLAUSE ("%llu") ");",
601                ACL_SUPER_CLAUSE_ARGS (type, field, resource,
602                                       current_credentials.uuid, trash)))
603     return 1;
604   return 0;
605 }
606 
607 /**
608  * @brief Test whether a user is the actual owner of a resource.
609  *
610  * @param[in]  type   Type of resource, for example "task".
611  * @param[in]  uuid   UUID of resource.
612  *
613  * @return 1 if user actually owns resource, else 0.
614  */
615 int
acl_user_is_owner(const char * type,const char * uuid)616 acl_user_is_owner (const char *type, const char *uuid)
617 {
618   int ret;
619   gchar *quoted_uuid;
620 
621   assert (uuid && current_credentials.uuid);
622 
623   quoted_uuid = g_strdup (uuid);
624   ret = sql_int ("SELECT count(*) FROM %ss"
625                  " WHERE uuid = '%s'"
626                  " AND owner = (SELECT users.id FROM users"
627                  "              WHERE users.uuid = '%s');",
628                  type,
629                  quoted_uuid,
630                  current_credentials.uuid);
631   g_free (quoted_uuid);
632 
633   return ret;
634 }
635 
636 /**
637  * @brief Test whether a user effectively owns a resource.
638  *
639  * A Super permissions can give a user effective ownership of another
640  * user's resource.
641  *
642  * @param[in]  type  Type of resource, for example "task".
643  * @param[in]  uuid      UUID of resource.
644  * @param[in]  trash     Whether the resource is in the trash.
645  *
646  * @return 1 if user owns resource, else 0.
647  */
648 int
acl_user_owns_uuid(const char * type,const char * uuid,int trash)649 acl_user_owns_uuid (const char *type, const char *uuid, int trash)
650 {
651   int ret;
652   gchar *quoted_uuid;
653 
654   assert (current_credentials.uuid);
655 
656   if ((strcmp (type, "nvt") == 0)
657       || (strcmp (type, "cve") == 0)
658       || (strcmp (type, "cpe") == 0)
659       || (strcmp (type, "ovaldef") == 0)
660       || (strcmp (type, "cert_bund_adv") == 0)
661       || (strcmp (type, "dfn_cert_adv") == 0))
662     return 1;
663 
664   if (acl_user_has_super_on (type, "uuid", uuid, 0))
665     return 1;
666 
667   quoted_uuid = sql_quote (uuid);
668   if (strcmp (type, "result") == 0)
669     ret = sql_int ("SELECT count(*) FROM results, reports"
670                    " WHERE results.uuid = '%s'"
671                    " AND results.report = reports.id"
672                    " AND (reports.owner = (SELECT users.id FROM users"
673                    "                       WHERE users.uuid = '%s'));",
674                    quoted_uuid,
675                    current_credentials.uuid);
676   else if (strcmp (type, "report") == 0)
677     ret = sql_int ("SELECT count(*) FROM reports"
678                    " WHERE uuid = '%s'"
679                    " AND (owner = (SELECT users.id FROM users"
680                    "               WHERE users.uuid = '%s'));",
681                    quoted_uuid,
682                    current_credentials.uuid);
683   else if (strcmp (type, "permission") == 0)
684     ret = sql_int ("SELECT count(*) FROM permissions%s"
685                    " WHERE uuid = '%s'"
686                    " AND ((owner IS NULL)"
687                    "      OR (owner = (SELECT users.id FROM users"
688                    "                   WHERE users.uuid = '%s')));",
689                    trash ? "_trash" : "",
690                    quoted_uuid,
691                    current_credentials.uuid);
692   else
693     ret = sql_int ("SELECT count(*) FROM %ss%s"
694                    " WHERE uuid = '%s'"
695                    "%s"
696                    " AND (owner = (SELECT users.id FROM users"
697                    "               WHERE users.uuid = '%s'));",
698                    type,
699                    (strcmp (type, "task") && trash) ? "_trash" : "",
700                    quoted_uuid,
701                    (strcmp (type, "task")
702                      ? ""
703                      : (trash ? " AND hidden = 2" : " AND hidden < 2")),
704                    current_credentials.uuid);
705   g_free (quoted_uuid);
706 
707   return ret;
708 }
709 
710 /**
711  * @brief Test whether a user effectively owns a resource.
712  *
713  * A Super permissions can give a user effective ownership of another
714  * user's resource.
715  *
716  * @param[in]  type      Type of resource, for example "task".
717  * @param[in]  resource  Resource.
718  * @param[in]  trash     Whether the resource is in the trash.
719  *
720  * @return 1 if user owns resource, else 0.
721  */
722 int
acl_user_owns(const char * type,resource_t resource,int trash)723 acl_user_owns (const char *type, resource_t resource, int trash)
724 {
725   int ret;
726 
727   assert (current_credentials.uuid);
728 
729   if ((strcmp (type, "nvt") == 0)
730       || (strcmp (type, "cve") == 0)
731       || (strcmp (type, "cpe") == 0)
732       || (strcmp (type, "ovaldef") == 0)
733       || (strcmp (type, "cert_bund_adv") == 0)
734       || (strcmp (type, "dfn_cert_adv") == 0))
735     return 1;
736 
737   if (acl_user_has_super_on_resource (type, "id", resource, trash))
738     return 1;
739 
740   if (strcmp (type, "result") == 0)
741     ret = sql_int ("SELECT count(*) FROM results, reports"
742                    " WHERE results.id = %llu"
743                    " AND results.report = reports.id"
744                    " AND (reports.owner = (SELECT users.id FROM users"
745                    "                       WHERE users.uuid = '%s'));",
746                    resource,
747                    current_credentials.uuid);
748   else
749     ret = sql_int ("SELECT count(*) FROM %ss%s"
750                    " WHERE id = %llu"
751                    "%s"
752                    " AND (owner = (SELECT users.id FROM users"
753                    "               WHERE users.uuid = '%s'));",
754                    type,
755                    (strcmp (type, "task") && trash) ? "_trash" : "",
756                    resource,
757                    (strcmp (type, "task")
758                      ? ""
759                      : (trash ? " AND hidden = 2" : " AND hidden < 2")),
760                    current_credentials.uuid);
761 
762   return ret;
763 }
764 
765 /**
766  * @brief Test whether a user effectively owns a resource.
767  *
768  * A Super permissions can give a user effective ownership of another
769  * user's resource.
770  *
771  * @param[in]  type  Type of resource, for example "task".
772  * @param[in]  uuid  UUID of resource.
773  *
774  * @return 1 if user owns resource, else 0.
775  */
776 int
acl_user_owns_trash_uuid(const char * type,const char * uuid)777 acl_user_owns_trash_uuid (const char *type, const char *uuid)
778 {
779   int ret;
780   gchar *quoted_uuid;
781 
782   assert (current_credentials.uuid);
783   assert (type && strcmp (type, "task"));
784 
785   if (acl_user_has_super_on (type, "uuid", uuid, 1))
786     return 1;
787 
788   quoted_uuid = sql_quote (uuid);
789   ret = sql_int ("SELECT count(*) FROM %ss_trash"
790                  " WHERE uuid = '%s'"
791                  " AND (owner = (SELECT users.id FROM users"
792                  "               WHERE users.uuid = '%s'));",
793                  type,
794                  quoted_uuid,
795                  current_credentials.uuid);
796   g_free (quoted_uuid);
797 
798   return ret;
799 }
800 
801 /**
802  * @brief Test whether the user may access a resource.
803  *
804  * @param[in]  type      Type of resource, for example "task".
805  * @param[in]  uuid      UUID of resource.
806  * @param[in]  permission       Permission.
807  * @param[in]  trash            Whether the resource is in the trash.
808  *
809  * @return 1 if user may access resource, else 0.
810  */
811 int
acl_user_has_access_uuid(const char * type,const char * uuid,const char * permission,int trash)812 acl_user_has_access_uuid (const char *type, const char *uuid,
813                           const char *permission, int trash)
814 {
815   int ret, get;
816   char *uuid_task;
817   gchar *quoted_permission, *quoted_uuid;
818 
819   assert (current_credentials.uuid);
820 
821   if (permission && (valid_gmp_command (permission) == 0))
822     return 0;
823 
824   if (!strcmp (current_credentials.uuid,  ""))
825     return 1;
826 
827   /* The Super case is checked here. */
828   ret = acl_user_owns_uuid (type, uuid, trash);
829   if (ret)
830     return ret;
831 
832   if (trash)
833     /* For simplicity, trashcan items are visible only to their owners. */
834     return 0;
835 
836   quoted_uuid = sql_quote (uuid);
837   if (strcasecmp (type, "report") == 0)
838     {
839       task_t task;
840       report_t report;
841 
842       switch (sql_int64 (&report,
843                          "SELECT id FROM reports WHERE uuid = '%s';",
844                          quoted_uuid))
845         {
846           case 0:
847             break;
848           case 1:        /* Too few rows in result of query. */
849             g_free (quoted_uuid);
850             return 0;
851             break;
852           default:       /* Programming error. */
853             assert (0);
854           case -1:
855             g_free (quoted_uuid);
856             return 0;
857             break;
858         }
859 
860       report_task (report, &task);
861       if (task == 0)
862         {
863           g_free (quoted_uuid);
864           return 0;
865         }
866       task_uuid (task, &uuid_task);
867     }
868   else if (strcasecmp (type, "result") == 0)
869     {
870       task_t task;
871 
872       switch (sql_int64 (&task,
873                          "SELECT task FROM results WHERE uuid = '%s';",
874                          uuid))
875         {
876           case 0:
877             break;
878           case 1:        /* Too few rows in result of query. */
879             g_free (quoted_uuid);
880             return 0;
881             break;
882           default:       /* Programming error. */
883             assert (0);
884           case -1:
885             g_free (quoted_uuid);
886             return 0;
887             break;
888         }
889 
890       task_uuid (task, &uuid_task);
891     }
892   else
893     uuid_task = NULL;
894 
895   if ((strcmp (type, "permission") == 0)
896       && ((permission == NULL)
897           || (strlen (permission) > 3 && strncmp (permission, "get", 3) == 0)))
898     {
899       ret = sql_int ("SELECT count(*) FROM permissions"
900                      /* Any permission implies 'get'. */
901                      " WHERE (resource_uuid = '%s'"
902                      /* Users may view any permissions that affect them. */
903                      "        OR uuid = '%s')"
904                      " AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
905                      " AND ((subject_type = 'user'"
906                      "       AND subject"
907                      "           = (SELECT id FROM users"
908                      "              WHERE users.uuid = '%s'))"
909                      "      OR (subject_type = 'group'"
910                      "          AND subject"
911                      "              IN (SELECT DISTINCT \"group\""
912                      "                  FROM group_users"
913                      "                  WHERE \"user\" = (SELECT id"
914                      "                                    FROM users"
915                      "                                    WHERE users.uuid"
916                      "                                          = '%s')))"
917                      "      OR (subject_type = 'role'"
918                      "          AND subject"
919                      "              IN (SELECT DISTINCT role"
920                      "                  FROM role_users"
921                      "                  WHERE \"user\" = (SELECT id"
922                      "                                    FROM users"
923                      "                                    WHERE users.uuid"
924                      "                                          = '%s'))));",
925                      uuid_task ? uuid_task : quoted_uuid,
926                      uuid_task ? uuid_task : quoted_uuid,
927                      current_credentials.uuid,
928                      current_credentials.uuid,
929                      current_credentials.uuid);
930       free (uuid_task);
931       g_free (quoted_uuid);
932       return ret;
933     }
934   else if (strcmp (type, "permission") == 0)
935     {
936       /* There are no "permissions on permissions", so if a user does not
937        * effectively own a permission, there's no way for the user to access
938        * the permission. */
939       free (uuid_task);
940       g_free (quoted_uuid);
941       return 0;
942     }
943 
944   get = (permission == NULL
945          || (strlen (permission) > 3 && strncmp (permission, "get", 3) == 0));
946   quoted_permission = sql_quote (permission ? permission : "");
947 
948   ret = sql_int ("SELECT count(*) FROM permissions"
949                  " WHERE resource_uuid = '%s'"
950                  " AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
951                  " AND ((subject_type = 'user'"
952                  "       AND subject"
953                  "           = (SELECT id FROM users"
954                  "              WHERE users.uuid = '%s'))"
955                  "      OR (subject_type = 'group'"
956                  "          AND subject"
957                  "              IN (SELECT DISTINCT \"group\""
958                  "                  FROM group_users"
959                  "                  WHERE \"user\" = (SELECT id"
960                  "                                    FROM users"
961                  "                                    WHERE users.uuid"
962                  "                                          = '%s')))"
963                  "      OR (subject_type = 'role'"
964                  "          AND subject"
965                  "              IN (SELECT DISTINCT role"
966                  "                  FROM role_users"
967                  "                  WHERE \"user\" = (SELECT id"
968                  "                                    FROM users"
969                  "                                    WHERE users.uuid"
970                  "                                          = '%s'))))"
971                  " %s%s%s;",
972                  uuid_task ? uuid_task : quoted_uuid,
973                  current_credentials.uuid,
974                  current_credentials.uuid,
975                  current_credentials.uuid,
976                  (get ? "" : "AND name = '"),
977                  (get ? "" : quoted_permission),
978                  (get ? "" : "'"));
979 
980   free (uuid_task);
981   g_free (quoted_permission);
982   g_free (quoted_uuid);
983   return ret;
984 }
985 
986 /**
987  * @brief Generate the ownership part of an SQL WHERE clause for a given user.
988  *
989  * @param[in]  user_id         UUID of user.  "" can be used to rely on
990  *                             user_sql alone, except when type is "permission".
991  * @param[in]  user_sql        SQL to get user.
992  * @param[in]  type            Type of resource.
993  * @param[in]  get             GET data.
994  * @param[in]  owned           Only get items accessible by the given user.
995  * @param[in]  owner_filter    Owner filter keyword.
996  * @param[in]  resource        Resource.
997  * @param[in]  permissions     Permissions.
998  * @param[in]  with_optional   Whether the WITH clause is optional.
999  * @param[in]  with_prefix     Optional prefix for WITH subqueries.
1000  * @param[out] with            Address for WITH clause if allowed, else NULL.
1001  *
1002  * @return Newly allocated owned clause.
1003  */
1004 static gchar *
acl_where_owned_user(const char * user_id,const char * user_sql,const char * type,const get_data_t * get,int owned,const gchar * owner_filter,resource_t resource,array_t * permissions,int with_optional,const char * with_prefix,gchar ** with)1005 acl_where_owned_user (const char *user_id, const char *user_sql,
1006                       const char *type, const get_data_t *get, int owned,
1007                       const gchar *owner_filter, resource_t resource,
1008                       array_t *permissions, int with_optional,
1009                       const char *with_prefix, gchar **with)
1010 {
1011   gchar *owned_clause, *filter_owned_clause;
1012   GString *permission_or;
1013   int table_trash;
1014   guint index;
1015 
1016   if (with)
1017     {
1018       *with = g_strdup_printf
1019                ("WITH %spermissions_subject"
1020                 "     AS (SELECT * FROM permissions"
1021                 "         WHERE subject_location"
1022                 "               = " G_STRINGIFY (LOCATION_TABLE)
1023                 "         AND ((subject_type = 'user'"
1024                 "               AND subject"
1025                 "                   = (%s))"
1026                 "              OR (subject_type = 'group'"
1027                 "                  AND subject"
1028                 "                      IN (SELECT DISTINCT \"group\""
1029                 "                          FROM group_users"
1030                 "                          WHERE \"user\""
1031                 "                                = (%s)))"
1032                 "              OR (subject_type = 'role'"
1033                 "                  AND subject"
1034                 "                      IN (SELECT DISTINCT role"
1035                 "                          FROM role_users"
1036                 "                          WHERE \"user\""
1037                 "                                = (%s))))),"
1038                 "     %ssuper_on_users"
1039                 "     AS (SELECT DISTINCT *"
1040                 "         FROM (SELECT resource FROM %spermissions_subject"
1041                 "               WHERE name = 'Super'"
1042                 "               AND resource_type = 'user'"
1043                 "               UNION"
1044                 "               SELECT \"user\" FROM role_users"
1045                 "               WHERE role"
1046                 "                     IN (SELECT resource"
1047                 "                         FROM %spermissions_subject"
1048                 "                         WHERE name = 'Super'"
1049                 "                         AND resource_type = 'role')"
1050                 "               UNION"
1051                 "               SELECT \"user\" FROM group_users"
1052                 "               WHERE \"group\""
1053                 "                     IN (SELECT resource"
1054                 "                         FROM %spermissions_subject"
1055                 "                         WHERE name = 'Super'"
1056                 "                         AND resource_type = 'group'))"
1057                 "              AS all_users)",
1058                 with_prefix ? with_prefix : "",
1059                 user_sql,
1060                 user_sql,
1061                 user_sql,
1062                 with_prefix ? with_prefix : "",
1063                 with_prefix ? with_prefix : "",
1064                 with_prefix ? with_prefix : "",
1065                 with_prefix ? with_prefix : "");
1066     }
1067 
1068   if (owned == 0)
1069     {
1070       if (with_optional && with)
1071         {
1072           g_free (*with);
1073           *with = NULL;
1074         }
1075       return g_strdup (" t ()");
1076     }
1077 
1078   permission_or = g_string_new ("");
1079   index = 0;
1080   if (permissions == NULL || permissions->len == 0)
1081     {
1082       /* Treat filters with no permissions keyword as "any". */
1083       permission_or = g_string_new ("t ()");
1084       index = 1;
1085     }
1086   else if (permissions)
1087     for (; index < permissions->len; index++)
1088       {
1089         gchar *permission, *quoted;
1090         permission = (gchar*) g_ptr_array_index (permissions, index);
1091         if (strcasecmp (permission, "any") == 0)
1092           {
1093             g_string_free (permission_or, TRUE);
1094             permission_or = g_string_new ("t ()");
1095             index = 1;
1096             break;
1097           }
1098         quoted = sql_quote (permission);
1099         if (index == 0)
1100           g_string_append_printf (permission_or, "name = '%s'", quoted);
1101         else
1102           g_string_append_printf (permission_or, " OR name = '%s'",
1103                                   quoted);
1104         g_free (quoted);
1105       }
1106 
1107   table_trash = get->trash && strcasecmp (type, "task");
1108   if (resource || (user_id == NULL))
1109     owned_clause
1110      = g_strdup (" (t ())");
1111   else if (with)
1112     {
1113       gchar *permission_clause;
1114 
1115       /* Caller supports WITH clause. */
1116 
1117       permission_clause = NULL;
1118       if (user_id && index)
1119         {
1120           gchar *clause;
1121           clause
1122            = g_strdup_printf ("OR EXISTS"
1123                               " (SELECT id FROM %spermissions_subject"
1124                               "  WHERE resource = %ss%s.id"
1125                               "  AND resource_type = '%s'"
1126                               "  AND resource_location = %i"
1127                               "  AND (%s))",
1128                               with_prefix ? with_prefix : "",
1129                               type,
1130                               get->trash && strcmp (type, "task") ? "_trash" : "",
1131                               type,
1132                               get->trash ? LOCATION_TRASH : LOCATION_TABLE,
1133                               permission_or->str);
1134 
1135           if (strcmp (type, "report") == 0)
1136             permission_clause
1137              = g_strdup_printf ("%s"
1138                                 " OR EXISTS"
1139                                 " (SELECT id FROM %spermissions_subject"
1140                                 "  WHERE resource = reports%s.task"
1141                                 "  AND resource_type = 'task'"
1142                                 "  AND (%s))",
1143                                 clause,
1144                                 with_prefix ? with_prefix : "",
1145                                 get->trash ? "_trash" : "",
1146                                 permission_or->str);
1147           else if (strcmp (type, "result") == 0)
1148             permission_clause
1149              = g_strdup_printf ("%s"
1150                                 " OR EXISTS"
1151                                 " (SELECT id FROM %spermissions_subject"
1152                                 "  WHERE resource = results%s.task"
1153                                 "  AND resource_type = 'task'"
1154                                 "  AND (%s))",
1155                                 clause,
1156                                 with_prefix ? with_prefix : "",
1157                                 get->trash ? "_trash" : "",
1158                                 permission_or->str);
1159 
1160           if ((strcmp (type, "report") == 0)
1161               || (strcmp (type, "result") == 0))
1162             g_free (clause);
1163           else
1164             permission_clause = clause;
1165         }
1166 
1167       if (strcmp (type, "permission") == 0)
1168         {
1169           int admin;
1170           assert (strcmp (user_id, ""));
1171           admin = acl_user_can_everything (user_id);
1172           /* A user sees permissions that involve the user.  Admin users also
1173            * see all higher level permissions. */
1174           owned_clause
1175            = g_strdup_printf (/* Either the user is the owner. */
1176                               " ((permissions%s.owner = (%s))"
1177                               /* Or, for admins, it's a global permission. */
1178                               "  %s"
1179                               /* Or the permission applies to the user. */
1180                               "  OR (%i = 0" /* Skip for trash. */
1181                               "      AND (permissions%s.subject_type = 'user'"
1182                               "           AND permissions%s.subject_location"
1183                               "               = " G_STRINGIFY (LOCATION_TABLE)
1184                               "           AND permissions%s.subject"
1185                               "               = (%s)))"
1186                               /* Or the permission applies to the user's group. */
1187                               "  OR (%i = 0" /* Skip for trash. */
1188                               "      AND (permissions%s.subject_type = 'group'"
1189                               "           AND permissions%s.subject_location"
1190                               "               = " G_STRINGIFY (LOCATION_TABLE)
1191                               "           AND permissions%s.subject"
1192                               "               IN (SELECT DISTINCT \"group\""
1193                               "                   FROM group_users"
1194                               "                   WHERE \"user\" = (%s))))"
1195                               /* Or the permission applies to the user's role. */
1196                               "  OR (%i = 0" /* Skip for trash. */
1197                               "      AND (permissions%s.subject_type = 'role'"
1198                               "           AND permissions%s.subject_location"
1199                               "               = " G_STRINGIFY (LOCATION_TABLE)
1200                               "           AND permissions%s.subject"
1201                               "               IN (SELECT DISTINCT role"
1202                               "                   FROM role_users"
1203                               "                   WHERE \"user\" = (%s))))"
1204                               /* Or the user has super permission on all. */
1205                               "  OR EXISTS (SELECT * FROM %spermissions_subject"
1206                               "             WHERE name = 'Super'"
1207                               "             AND (resource = 0))"
1208                               /* Or the user has super permission on the owner,
1209                                * (directly, via the role, or via the group). */
1210                               "  OR permissions%s.owner IN"
1211                               "      (SELECT * FROM %ssuper_on_users)"
1212                               "  %s)",
1213                               get->trash ? "_trash" : "",
1214                               user_sql,
1215                               admin
1216                                ? (get->trash
1217                                    ? "OR (permissions_trash.owner IS NULL)"
1218                                    : "OR (permissions.owner IS NULL)")
1219                                : "",
1220                               get->trash,
1221                               table_trash ? "_trash" : "",
1222                               table_trash ? "_trash" : "",
1223                               table_trash ? "_trash" : "",
1224                               user_sql,
1225                               get->trash,
1226                               table_trash ? "_trash" : "",
1227                               table_trash ? "_trash" : "",
1228                               table_trash ? "_trash" : "",
1229                               user_sql,
1230                               get->trash,
1231                               table_trash ? "_trash" : "",
1232                               table_trash ? "_trash" : "",
1233                               table_trash ? "_trash" : "",
1234                               user_sql,
1235                               with_prefix ? with_prefix : "",
1236                               table_trash ? "_trash" : "",
1237                               with_prefix ? with_prefix : "",
1238                               permission_clause ? permission_clause : "");
1239         }
1240       else
1241         /* Any resource type other than Permissions. */
1242         owned_clause
1243          = g_strdup_printf (/* Either the user is the owner. */
1244                             " ((%ss%s.owner"
1245                             "   = (%s))"
1246                             /* Or the user has super permission on all. */
1247                             "  OR EXISTS (SELECT * FROM %spermissions_subject"
1248                             "             WHERE name = 'Super'"
1249                             "             AND (resource = 0))"
1250                             /* Or the user has super permission on the owner,
1251                              * (directly, via the role, or via the group). */
1252                             "  OR %ss%s.owner IN (SELECT *"
1253                             "                     FROM %ssuper_on_users)"
1254                             "  %s)",
1255                             type,
1256                             table_trash ? "_trash" : "",
1257                             user_sql,
1258                             with_prefix ? with_prefix : "",
1259                             type,
1260                             table_trash ? "_trash" : "",
1261                             with_prefix ? with_prefix : "",
1262                             permission_clause ? permission_clause : "");
1263 
1264       g_free (permission_clause);
1265     }
1266   else
1267     {
1268       gchar *permission_clause;
1269 
1270       /* Caller does not support WITH.
1271        *
1272        * The result_overrides view is the last caller that requires this case,
1273        * because it cannot support this WITH mechanism. */
1274 
1275       permission_clause = NULL;
1276 
1277       /* Check on index is because default is owner and global, for backward
1278        * compatibility. */
1279       if (user_id && index)
1280         {
1281           gchar *clause;
1282           clause
1283            = g_strdup_printf ("OR EXISTS"
1284                               " (SELECT id FROM permissions"
1285                               "  WHERE resource = %ss%s.id"
1286                               "  AND resource_type = '%s'"
1287                               "  AND resource_location = %i"
1288                               "  AND subject_location"
1289                               "      = " G_STRINGIFY (LOCATION_TABLE)
1290                               "  AND ((subject_type = 'user'"
1291                               "        AND subject"
1292                               "            = (%s))"
1293                               "       OR (subject_type = 'group'"
1294                               "           AND subject"
1295                               "               IN (SELECT DISTINCT \"group\""
1296                               "                   FROM group_users"
1297                               "                   WHERE \"user\""
1298                               "                         = (%s)))"
1299                               "       OR (subject_type = 'role'"
1300                               "           AND subject"
1301                               "               IN (SELECT DISTINCT role"
1302                               "                   FROM role_users"
1303                               "                   WHERE \"user\""
1304                               "                         = (%s))))"
1305                               "  AND (%s))",
1306                               type,
1307                               get->trash && strcmp (type, "task") ? "_trash" : "",
1308                               type,
1309                               get->trash ? LOCATION_TRASH : LOCATION_TABLE,
1310                               user_sql,
1311                               user_sql,
1312                               user_sql,
1313                               permission_or->str);
1314 
1315           if (strcmp (type, "report") == 0)
1316             permission_clause
1317              = g_strdup_printf ("%s"
1318                                 " OR EXISTS"
1319                                 " (SELECT id FROM permissions"
1320                                 "  WHERE resource = reports%s.task"
1321                                 "  AND resource_type = 'task'"
1322                                 "  AND subject_location"
1323                                 "      = " G_STRINGIFY (LOCATION_TABLE)
1324                                 "  AND ((subject_type = 'user'"
1325                                 "        AND subject"
1326                                 "            = (%s))"
1327                                 "       OR (subject_type = 'group'"
1328                                 "           AND subject"
1329                                 "               IN (SELECT DISTINCT \"group\""
1330                                 "                   FROM group_users"
1331                                 "                   WHERE \"user\""
1332                                 "                         = (%s)))"
1333                                 "       OR (subject_type = 'role'"
1334                                 "           AND subject"
1335                                 "               IN (SELECT DISTINCT role"
1336                                 "                   FROM role_users"
1337                                 "                   WHERE \"user\""
1338                                 "                         = (%s))))"
1339                                 "  AND (%s))",
1340                                 clause,
1341                                 get->trash ? "_trash" : "",
1342                                 user_sql,
1343                                 user_sql,
1344                                 user_sql,
1345                                 permission_or->str);
1346           else if (strcmp (type, "result") == 0)
1347             permission_clause
1348              = g_strdup_printf ("%s"
1349                                 " OR EXISTS"
1350                                 " (SELECT id FROM permissions"
1351                                 "  WHERE resource = results%s.task"
1352                                 "  AND resource_type = 'task'"
1353                                 "  AND subject_location"
1354                                 "      = " G_STRINGIFY (LOCATION_TABLE)
1355                                 "  AND ((subject_type = 'user'"
1356                                 "        AND subject"
1357                                 "            = (%s))"
1358                                 "       OR (subject_type = 'group'"
1359                                 "           AND subject"
1360                                 "               IN (SELECT DISTINCT \"group\""
1361                                 "                   FROM group_users"
1362                                 "                   WHERE \"user\""
1363                                 "                         = (%s)))"
1364                                 "       OR (subject_type = 'role'"
1365                                 "           AND subject"
1366                                 "               IN (SELECT DISTINCT role"
1367                                 "                   FROM role_users"
1368                                 "                   WHERE \"user\""
1369                                 "                         = (%s))))"
1370                                 "  AND (%s))",
1371                                 clause,
1372                                 get->trash ? "_trash" : "",
1373                                 user_sql,
1374                                 user_sql,
1375                                 user_sql,
1376                                 permission_or->str);
1377 
1378           if ((strcmp (type, "report") == 0)
1379               || (strcmp (type, "result") == 0))
1380             g_free (clause);
1381           else
1382             permission_clause = clause;
1383         }
1384 
1385       owned_clause
1386        = g_strdup_printf (/* Either the user is the owner. */
1387                           " ((%ss%s.owner"
1388                           "   = (%s))"
1389                           /* Or the user has super permission. */
1390                           "  OR EXISTS (SELECT * FROM permissions"
1391                           "             WHERE name = 'Super'"
1392                           /*                 Super on everyone. */
1393                           "             AND ((resource = 0)"
1394                           /*                 Super on other_user. */
1395                           "                  OR ((resource_type = 'user')"
1396                           "                      AND (resource = %ss%s.owner))"
1397                           /*                 Super on other_user's role. */
1398                           "                  OR ((resource_type = 'role')"
1399                           "                      AND (resource"
1400                           "                           IN (SELECT DISTINCT role"
1401                           "                               FROM role_users"
1402                           "                               WHERE \"user\""
1403                           "                                     = %ss%s.owner)))"
1404                           /*                 Super on other_user's group. */
1405                           "                  OR ((resource_type = 'group')"
1406                           "                      AND (resource"
1407                           "                           IN (SELECT DISTINCT \"group\""
1408                           "                               FROM group_users"
1409                           "                               WHERE \"user\""
1410                           "                                     = %ss%s.owner))))"
1411                           "             AND subject_location"
1412                           "                 = " G_STRINGIFY (LOCATION_TABLE)
1413                           "             AND ((subject_type = 'user'"
1414                           "                   AND subject"
1415                           "                       = (%s))"
1416                           "                  OR (subject_type = 'group'"
1417                           "                      AND subject"
1418                           "                          IN (SELECT DISTINCT \"group\""
1419                           "                              FROM group_users"
1420                           "                              WHERE \"user\""
1421                           "                                    = (%s)))"
1422                           "                  OR (subject_type = 'role'"
1423                           "                      AND subject"
1424                           "                          IN (SELECT DISTINCT role"
1425                           "                              FROM role_users"
1426                           "                              WHERE \"user\""
1427                           "                                    = (%s)))))"
1428                           "  %s)",
1429                           type,
1430                           table_trash ? "_trash" : "",
1431                           user_sql,
1432                           type,
1433                           table_trash ? "_trash" : "",
1434                           type,
1435                           table_trash ? "_trash" : "",
1436                           type,
1437                           table_trash ? "_trash" : "",
1438                           user_sql,
1439                           user_sql,
1440                           user_sql,
1441                           permission_clause ? permission_clause : "");
1442 
1443       g_free (permission_clause);
1444     }
1445 
1446   g_string_free (permission_or, TRUE);
1447 
1448   if (get->trash && (strcasecmp (type, "task") == 0))
1449     {
1450       gchar *new;
1451       new = g_strdup_printf (" (%ss.hidden = 2"
1452                              "  AND %s)",
1453                              type,
1454                              owned_clause);
1455       g_free (owned_clause);
1456       owned_clause = new;
1457     }
1458 
1459   if (owner_filter == NULL
1460       || (owner_filter && (strcmp (owner_filter, "any") == 0)))
1461     filter_owned_clause = g_strdup (owned_clause);
1462   else if (owner_filter && strcmp (owner_filter, ""))
1463     {
1464       gchar *quoted;
1465       quoted = sql_quote (owner_filter);
1466       filter_owned_clause = g_strdup_printf ("(owner = (SELECT id"
1467                                              "          FROM users"
1468                                              "          WHERE name = '%s')"
1469                                              " AND %s)",
1470                                              quoted,
1471                                              owned_clause);
1472       g_free (quoted);
1473     }
1474   else
1475     filter_owned_clause = g_strdup_printf ("((owner = (%s))"
1476                                            " AND %s)",
1477                                            user_sql,
1478                                            owned_clause);
1479 
1480   g_free (owned_clause);
1481 
1482   return filter_owned_clause;
1483 }
1484 
1485 /**
1486  * @brief Generate the ownership part of an SQL WHERE clause.
1487  *
1488  * @param[in]  type            Type of resource.
1489  * @param[in]  get             GET data.
1490  * @param[in]  owned           Only get items owned by the current user.
1491  * @param[in]  owner_filter    Owner filter keyword.
1492  * @param[in]  resource        Resource.
1493  * @param[in]  permissions     Permissions.
1494  * @param[in]  with_optional   Whether permissions WITH clauses are optional.
1495  * @param[out] with            Address for WITH clause if allowed, else NULL.
1496  *
1497  * @return Newly allocated owned clause.
1498  */
1499 gchar *
acl_where_owned(const char * type,const get_data_t * get,int owned,const gchar * owner_filter,resource_t resource,array_t * permissions,int with_optional,gchar ** with)1500 acl_where_owned (const char *type, const get_data_t *get, int owned,
1501                  const gchar *owner_filter, resource_t resource,
1502                  array_t *permissions, int with_optional, gchar **with)
1503 {
1504   gchar *ret, *user_sql;
1505   if (current_credentials.uuid)
1506     user_sql = g_strdup_printf ("SELECT id FROM users WHERE users.uuid = '%s'",
1507                                 current_credentials.uuid);
1508   else
1509     user_sql = NULL;
1510   ret = acl_where_owned_user (current_credentials.uuid, user_sql, type, get,
1511                               owned, owner_filter, resource, permissions,
1512                               with_optional, NULL, with);
1513   g_free (user_sql);
1514   return ret;
1515 }
1516 
1517 /**
1518  * @brief Generate ownership part of WHERE, for getting a type of resource.
1519  *
1520  * @param[in]  type      Type of resource.
1521  * @param[in]  user_sql  SQL for getting user.  If NULL SQL will be for current
1522  *                       user.
1523  * @param[in]  with_prefix  Optional prefix for WITH clause.
1524  * @param[out] with      Return location for WITH preselection clause if
1525  *                       desired, else NULL.
1526  *
1527  * @return Newly allocated owned clause.
1528  */
1529 gchar *
acl_where_owned_for_get(const char * type,const char * user_sql,const char * with_prefix,gchar ** with)1530 acl_where_owned_for_get (const char *type, const char *user_sql,
1531                          const char *with_prefix, gchar **with)
1532 {
1533   gchar *owned_clause;
1534   get_data_t get;
1535   array_t *permissions;
1536   gchar *user_sql_new;
1537 
1538   if (user_sql)
1539     user_sql_new = g_strdup (user_sql);
1540   else if (current_credentials.uuid)
1541     user_sql_new = g_strdup_printf ("SELECT id FROM users WHERE users.uuid = '%s'",
1542                                     current_credentials.uuid);
1543   else
1544     user_sql_new = NULL;
1545 
1546   get.trash = 0;
1547   permissions = make_array ();
1548   array_add (permissions, g_strdup_printf ("get_%ss", type));
1549   owned_clause = acl_where_owned_user (current_credentials.uuid
1550                                         ? current_credentials.uuid
1551                                         /* Use user_sql_new. */
1552                                         : "",
1553                                        user_sql_new,
1554                                        type,
1555                                        &get,
1556                                        1,              /* Do owner checks. */
1557                                        "any",
1558                                        0,              /* Resource. */
1559                                        permissions,
1560                                        0,              /* WITH not optional */
1561                                        with_prefix,
1562                                        with);
1563   array_free (permissions);
1564   g_free (user_sql_new);
1565 
1566   return owned_clause;
1567 }
1568 
1569 /**
1570  * @brief Get an SQL values expression of users that can get a resource
1571  *
1572  * @param[in]  type         The resource type.
1573  * @param[in]  resource_id  The UUID of the resource.
1574  * @param[in]  users_where  Optional clause to limit users.
1575  *
1576  * @return  Newly allocated SQL string or NULL if no users have access.
1577  */
1578 
1579 gchar *
acl_users_with_access_sql(const char * type,const char * resource_id,const char * users_where)1580 acl_users_with_access_sql (const char *type, const char *resource_id,
1581                            const char *users_where)
1582 {
1583   GString *users_string;
1584   int users_count = 0;
1585   gchar *old_user_id, *command;
1586   iterator_t users;
1587 
1588   old_user_id = current_credentials.uuid;
1589   init_iterator (&users, "SELECT id, uuid FROM users WHERE %s;",
1590                  users_where ? users_where : "t()");
1591 
1592   users_string = g_string_new ("(VALUES ");
1593 
1594   command = g_strdup_printf ("get_%ss", type);
1595 
1596   while (next (&users))
1597     {
1598       current_credentials.uuid = g_strdup (iterator_string (&users, 1));
1599       manage_session_init (current_credentials.uuid);
1600       if (acl_user_has_access_uuid (type, resource_id, command, 0))
1601         {
1602           if (users_count)
1603             g_string_append (users_string,
1604                              ", ");
1605 
1606           g_string_append_printf (users_string,
1607                                   "(%llu)",
1608                                   iterator_int64 (&users, 0));
1609           users_count ++;
1610         }
1611       g_free (current_credentials.uuid);
1612     }
1613   g_string_append(users_string, ")");
1614   cleanup_iterator (&users);
1615 
1616   current_credentials.uuid = old_user_id;
1617   manage_session_init (old_user_id);
1618 
1619   g_free (command);
1620 
1621   if (users_count == 0)
1622     {
1623       g_string_free (users_string, TRUE);
1624       return NULL;
1625     }
1626 
1627   return g_string_free (users_string, FALSE);
1628 
1629 }
1630 
1631 /**
1632  * @brief Get a static SQL condition selecting users that can get a resource
1633  *
1634  * @param[in]  type         The resource type.
1635  * @param[in]  resource_id  The UUID of the resource.
1636  * @param[in]  users_where  Optional clause to limit users.
1637  * @param[in]  user_expr    Expression for the user, e.g. the column name.
1638  *
1639  * @return  Newly allocated SQL string or NULL if no users have access.
1640  */
1641 
1642 gchar *
acl_users_with_access_where(const char * type,const char * resource_id,const char * users_where,const char * user_expr)1643 acl_users_with_access_where (const char *type, const char *resource_id,
1644                              const char *users_where, const char* user_expr)
1645 {
1646   gchar *values, *ret;
1647   assert (user_expr);
1648   values = acl_users_with_access_sql (type, resource_id, users_where);
1649   if (values)
1650     ret = g_strdup_printf ("%s IN %s", user_expr, values);
1651   else
1652     ret = g_strdup ("NOT t()");
1653   g_free (values);
1654   return ret;
1655 }
1656