1/* authz_info.c : Information derived from authz settings.
2 *
3 * ====================================================================
4 *    Licensed to the Apache Software Foundation (ASF) under one
5 *    or more contributor license agreements.  See the NOTICE file
6 *    distributed with this work for additional information
7 *    regarding copyright ownership.  The ASF licenses this file
8 *    to you under the Apache License, Version 2.0 (the
9 *    "License"); you may not use this file except in compliance
10 *    with the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 *    Unless required by applicable law or agreed to in writing,
15 *    software distributed under the License is distributed on an
16 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 *    KIND, either express or implied.  See the License for the
18 *    specific language governing permissions and limitations
19 *    under the License.
20 * ====================================================================
21 */
22
23#include <apr_hash.h>
24#include <apr_pools.h>
25#include <apr_tables.h>
26
27#include "svn_hash.h"
28
29#include "svn_private_config.h"
30
31#include "authz.h"
32
33
34svn_boolean_t
35svn_authz__acl_applies_to_repo(const authz_acl_t *acl,
36                               const char *repos)
37{
38  /* The repository name must match the one in the rule, iff the rule
39     was defined for a specific repository. */
40  return (0 == strcmp(acl->rule.repos, AUTHZ_ANY_REPOSITORY))
41      || (0 == strcmp(repos, acl->rule.repos));
42}
43
44svn_boolean_t
45svn_authz__get_acl_access(authz_access_t *access_p,
46                          const authz_acl_t *acl,
47                          const char *user, const char *repos)
48{
49  authz_access_t access;
50  svn_boolean_t has_access;
51  int i;
52
53  /* The repository name must match the one in the rule, iff the rule
54     was defined for a specific repository. */
55  if (!svn_authz__acl_applies_to_repo(acl, repos))
56    return FALSE;
57
58  /* Check anonymous access first. */
59  if (!user || 0 == strcmp(user, AUTHZ_ANONYMOUS_USER))
60    {
61      if (!acl->has_anon_access)
62        return FALSE;
63
64      if (access_p)
65        *access_p = acl->anon_access;
66      return TRUE;
67    }
68
69  /* Get the access rights for all authenticated users. */
70  has_access = acl->has_authn_access;
71  access = (has_access ? acl->authn_access : authz_access_none);
72
73  /* Scan the ACEs in the ACL and merge the access rights. */
74  for (i = 0; i < acl->user_access->nelts; ++i)
75    {
76      const authz_ace_t *const ace =
77        &APR_ARRAY_IDX(acl->user_access, i, authz_ace_t);
78      const svn_boolean_t match =
79        ((ace->members && svn_hash_gets(ace->members, user))
80         || (!ace->members && 0 == strcmp(user, ace->name)));
81
82      if (!match != !ace->inverted) /* match XNOR ace->inverted */
83        {
84          access |= ace->access;
85          has_access = TRUE;
86        }
87    }
88
89  if (access_p)
90    *access_p = access;
91  return has_access;
92}
93
94/* Set *RIGHTS_P to the combination of LHS and RHS, i.e. intersect the
95 * minimal rights and join the maximum rights.
96 */
97static void
98combine_rights(authz_rights_t *rights_p,
99               const authz_rights_t *lhs,
100               const authz_rights_t *rhs)
101{
102  rights_p->min_access = lhs->min_access & rhs->min_access;
103  rights_p->max_access = lhs->max_access | rhs->max_access;
104}
105
106
107/* Given GLOBAL_RIGHTS and a repository name REPOS, set *RIGHTS_P to
108 * to the actual accumulated rights defined for that repository.
109 * Return TRUE if these rights were defined explicitly.
110 */
111static svn_boolean_t
112resolve_global_rights(authz_rights_t *rights_p,
113                      const authz_global_rights_t *global_rights,
114                      const char *repos)
115{
116  if (0 == strcmp(repos, AUTHZ_ANY_REPOSITORY))
117    {
118      /* Return the accumulated rights that are not repository-specific. */
119      *rights_p = global_rights->any_repos_rights;
120      return TRUE;
121    }
122  else
123    {
124      /* Check if we have explicit rights for this repository. */
125      const authz_rights_t *const rights =
126        svn_hash_gets(global_rights->per_repos_rights, repos);
127
128      if (rights)
129        {
130          combine_rights(rights_p, rights, &global_rights->any_repos_rights);
131          return TRUE;
132        }
133    }
134
135  /* Fall-through: return the rights defined for "any" repository
136     because this user has no specific rules for this specific REPOS. */
137  *rights_p = global_rights->any_repos_rights;
138  return FALSE;
139}
140
141
142svn_boolean_t
143svn_authz__get_global_rights(authz_rights_t *rights_p,
144                             const authz_full_t *authz,
145                             const char *user, const char *repos)
146{
147  if (!user || 0 == strcmp(user, AUTHZ_ANONYMOUS_USER))
148    {
149      /* Check if we have explicit rights for anonymous access. */
150      if (authz->has_anon_rights)
151        {
152          return resolve_global_rights(rights_p, &authz->anon_rights, repos);
153        }
154      else
155        {
156          /* Return the implicit rights, i.e., none. */
157          rights_p->min_access = authz_access_none;
158          rights_p->max_access = authz_access_none;
159          return FALSE;
160        }
161    }
162  else
163    {
164      svn_boolean_t combine_user_rights = FALSE;
165      svn_boolean_t access = FALSE;
166
167      /* Check if we have explicit rights for this user. */
168      const authz_global_rights_t *const user_rights =
169        svn_hash_gets(authz->user_rights, user);
170
171      if (user_rights)
172        {
173          access = resolve_global_rights(rights_p, user_rights, repos);
174          combine_user_rights = TRUE;
175        }
176      else if (authz->has_neg_rights)
177        {
178          /* Check if inverted-rule rights apply */
179          access = resolve_global_rights(rights_p, &authz->neg_rights, repos);
180          combine_user_rights = TRUE;
181        }
182
183      /* Rights given to _any_ authenticated user may apply, too. */
184      if (authz->has_authn_rights)
185        {
186          authz_rights_t authn;
187          access |= resolve_global_rights(&authn, &authz->authn_rights, repos);
188
189          if (combine_user_rights)
190            combine_rights(rights_p, rights_p, &authn);
191          else
192            *rights_p = authn;
193        }
194
195      return access;
196    }
197}
198