1 /* authz.c : path-based access control
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
20 * ====================================================================
26 #include <apr_pools.h>
27 #include <apr_file_io.h>
30 #include "svn_pools.h"
31 #include "svn_error.h"
32 #include "svn_dirent_uri.h"
34 #include "svn_repos.h"
35 #include "svn_config.h"
36 #include "svn_ctype.h"
37 #include "private/svn_fspath.h"
43 /* Information for the config enumerators called during authz
45 struct authz_lookup_baton {
46 /* The authz configuration. */
49 /* The user to authorize. */
52 /* Explicitly granted rights. */
53 svn_repos_authz_access_t allow;
54 /* Explicitly denied rights. */
55 svn_repos_authz_access_t deny;
57 /* The rights required by the caller of the lookup. */
58 svn_repos_authz_access_t required_access;
60 /* The following are used exclusively in recursive lookups. */
62 /* The path in the repository (an fspath) to authorize. */
63 const char *repos_path;
64 /* repos_path prefixed by the repository name and a colon. */
65 const char *qualified_repos_path;
67 /* Whether, at the end of a recursive lookup, access is granted. */
71 /* Information for the config enumeration functions called during the
72 validation process. */
73 struct authz_validate_baton {
74 svn_config_t *config; /* The configuration file being validated. */
75 svn_error_t *err; /* The error being thrown out of the
76 enumerator, if any. */
79 /* Currently this structure is just a wrapper around a
88 /*** Checking access. ***/
90 /* Determine whether the REQUIRED access is granted given what authz
91 * to ALLOW or DENY. Return TRUE if the REQUIRED access is
94 * Access is granted either when no required access is explicitly
95 * denied (implicit grant), or when the required access is explicitly
96 * granted, overriding any denials.
99 authz_access_is_granted(svn_repos_authz_access_t allow,
100 svn_repos_authz_access_t deny,
101 svn_repos_authz_access_t required)
103 svn_repos_authz_access_t stripped_req =
104 required & (svn_authz_read | svn_authz_write);
106 if ((deny & required) == svn_authz_none)
108 else if ((allow & required) == stripped_req)
115 /* Decide whether the REQUIRED access has been conclusively
116 * determined. Return TRUE if the given ALLOW/DENY authz are
117 * conclusive regarding the REQUIRED authz.
119 * Conclusive determination occurs when any of the REQUIRED authz are
120 * granted or denied by ALLOW/DENY.
123 authz_access_is_determined(svn_repos_authz_access_t allow,
124 svn_repos_authz_access_t deny,
125 svn_repos_authz_access_t required)
127 if ((deny & required) || (allow & required))
133 /* Return TRUE is USER equals ALIAS. The alias definitions are in the
134 "aliases" sections of CFG. Use POOL for temporary allocations during
137 authz_alias_is_user(svn_config_t *cfg,
144 svn_config_get(cfg, &value, "aliases", alias, NULL);
148 if (strcmp(value, user) == 0)
155 /* Return TRUE if USER is in GROUP. The group definitions are in the
156 "groups" section of CFG. Use POOL for temporary allocations during
159 authz_group_contains_user(svn_config_t *cfg,
165 apr_array_header_t *list;
168 svn_config_get(cfg, &value, "groups", group, NULL);
170 list = svn_cstring_split(value, ",", TRUE, pool);
172 for (i = 0; i < list->nelts; i++)
174 const char *group_user = APR_ARRAY_IDX(list, i, char *);
176 /* If the 'user' is a subgroup, recurse into it. */
177 if (*group_user == '@')
179 if (authz_group_contains_user(cfg, &group_user[1],
184 /* If the 'user' is an alias, verify it. */
185 else if (*group_user == '&')
187 if (authz_alias_is_user(cfg, &group_user[1],
192 /* If the user matches, stop. */
193 else if (strcmp(user, group_user) == 0)
201 /* Determines whether an authz rule applies to the current
202 * user, given the name part of the rule's name-value pair
203 * in RULE_MATCH_STRING and the authz_lookup_baton object
204 * B with the username in question.
207 authz_line_applies_to_user(const char *rule_match_string,
208 struct authz_lookup_baton *b,
211 /* If the rule has an inversion, recurse and invert the result. */
212 if (rule_match_string[0] == '~')
213 return !authz_line_applies_to_user(&rule_match_string[1], b, pool);
215 /* Check for special tokens. */
216 if (strcmp(rule_match_string, "$anonymous") == 0)
217 return (b->user == NULL);
218 if (strcmp(rule_match_string, "$authenticated") == 0)
219 return (b->user != NULL);
221 /* Check for a wildcard rule. */
222 if (strcmp(rule_match_string, "*") == 0)
225 /* If we get here, then the rule is:
226 * - Not an inversion rule.
227 * - Not an authz token rule.
228 * - Not a wildcard rule.
230 * All that's left over is regular user or group specifications.
233 /* If the session is anonymous, then a user/group
234 * rule definitely won't match.
239 /* Process the rule depending on whether it is
240 * a user, alias or group rule.
242 if (rule_match_string[0] == '@')
243 return authz_group_contains_user(
244 b->config, &rule_match_string[1], b->user, pool);
245 else if (rule_match_string[0] == '&')
246 return authz_alias_is_user(
247 b->config, &rule_match_string[1], b->user, pool);
249 return (strcmp(b->user, rule_match_string) == 0);
253 /* Callback to parse one line of an authz file and update the
254 * authz_baton accordingly.
257 authz_parse_line(const char *name, const char *value,
258 void *baton, apr_pool_t *pool)
260 struct authz_lookup_baton *b = baton;
262 /* Stop if the rule doesn't apply to this user. */
263 if (!authz_line_applies_to_user(name, b, pool))
266 /* Set the access grants for the rule. */
267 if (strchr(value, 'r'))
268 b->allow |= svn_authz_read;
270 b->deny |= svn_authz_read;
272 if (strchr(value, 'w'))
273 b->allow |= svn_authz_write;
275 b->deny |= svn_authz_write;
281 /* Return TRUE iff the access rules in SECTION_NAME apply to PATH_SPEC
282 * (which is a repository name, colon, and repository fspath, such as
283 * "myrepos:/trunk/foo").
286 is_applicable_section(const char *path_spec,
287 const char *section_name)
289 apr_size_t path_spec_len = strlen(path_spec);
291 return ((strncmp(path_spec, section_name, path_spec_len) == 0)
292 && (path_spec[path_spec_len - 1] == '/'
293 || section_name[path_spec_len] == '/'
294 || section_name[path_spec_len] == '\0'));
298 /* Callback to parse a section and update the authz_baton if the
299 * section denies access to the subtree the baton describes.
302 authz_parse_section(const char *section_name, void *baton, apr_pool_t *pool)
304 struct authz_lookup_baton *b = baton;
305 svn_boolean_t conclusive;
307 /* Does the section apply to us? */
308 if (!is_applicable_section(b->qualified_repos_path, section_name)
309 && !is_applicable_section(b->repos_path, section_name))
312 /* Work out what this section grants. */
313 b->allow = b->deny = 0;
314 svn_config_enumerate2(b->config, section_name,
315 authz_parse_line, b, pool);
317 /* Has the section explicitly determined an access? */
318 conclusive = authz_access_is_determined(b->allow, b->deny,
321 /* Is access granted OR inconclusive? */
322 b->access = authz_access_is_granted(b->allow, b->deny,
326 /* As long as access isn't conclusively denied, carry on. */
331 /* Validate access to the given user for the given path. This
332 * function checks rules for exactly the given path, and first tries
333 * to access a section specific to the given repository before falling
334 * back to pan-repository rules.
336 * Update *access_granted to inform the caller of the outcome of the
337 * lookup. Return a boolean indicating whether the access rights were
338 * successfully determined.
341 authz_get_path_access(svn_config_t *cfg, const char *repos_name,
342 const char *path, const char *user,
343 svn_repos_authz_access_t required_access,
344 svn_boolean_t *access_granted,
347 const char *qualified_path;
348 struct authz_lookup_baton baton = { 0 };
353 /* Try to locate a repository-specific block first. */
354 qualified_path = apr_pstrcat(pool, repos_name, ":", path, (char *)NULL);
355 svn_config_enumerate2(cfg, qualified_path,
356 authz_parse_line, &baton, pool);
358 *access_granted = authz_access_is_granted(baton.allow, baton.deny,
361 /* If the first test has determined access, stop now. */
362 if (authz_access_is_determined(baton.allow, baton.deny,
366 /* No repository specific rule, try pan-repository rules. */
367 svn_config_enumerate2(cfg, path, authz_parse_line, &baton, pool);
369 *access_granted = authz_access_is_granted(baton.allow, baton.deny,
371 return authz_access_is_determined(baton.allow, baton.deny,
376 /* Validate access to the given user for the subtree starting at the
377 * given path. This function walks the whole authz file in search of
378 * rules applying to paths in the requested subtree which deny the
381 * As soon as one is found, or else when the whole ACL file has been
382 * searched, return the updated authorization status.
385 authz_get_tree_access(svn_config_t *cfg, const char *repos_name,
386 const char *path, const char *user,
387 svn_repos_authz_access_t required_access,
390 struct authz_lookup_baton baton = { 0 };
394 baton.required_access = required_access;
395 baton.repos_path = path;
396 baton.qualified_repos_path = apr_pstrcat(pool, repos_name,
397 ":", path, (char *)NULL);
398 /* Default to access granted if no rules say otherwise. */
401 svn_config_enumerate_sections2(cfg, authz_parse_section,
408 /* Callback to parse sections of the configuration file, looking for
409 any kind of granted access. Implements the
410 svn_config_section_enumerator2_t interface. */
412 authz_get_any_access_parser_cb(const char *section_name, void *baton,
415 struct authz_lookup_baton *b = baton;
417 /* Does the section apply to the query? */
418 if (section_name[0] == '/'
419 || strncmp(section_name, b->qualified_repos_path,
420 strlen(b->qualified_repos_path)) == 0)
422 b->allow = b->deny = svn_authz_none;
424 svn_config_enumerate2(b->config, section_name,
425 authz_parse_line, baton, pool);
426 b->access = authz_access_is_granted(b->allow, b->deny,
429 /* Continue as long as we don't find a determined, granted access. */
431 && authz_access_is_determined(b->allow, b->deny,
432 b->required_access));
439 /* Walk through the authz CFG to check if USER has the REQUIRED_ACCESS
440 * to any path within the REPOSITORY. Return TRUE if so. Use POOL
441 * for temporary allocations. */
443 authz_get_any_access(svn_config_t *cfg, const char *repos_name,
445 svn_repos_authz_access_t required_access,
448 struct authz_lookup_baton baton = { 0 };
452 baton.required_access = required_access;
453 baton.access = FALSE; /* Deny access by default. */
454 baton.repos_path = "/";
455 baton.qualified_repos_path = apr_pstrcat(pool, repos_name,
458 /* We could have used svn_config_enumerate2 for "repos_name:/".
459 * However, this requires access for root explicitly (which the user
460 * may not always have). So we end up enumerating the sections in
461 * the authz CFG and stop on the first match with some access for
463 svn_config_enumerate_sections2(cfg, authz_get_any_access_parser_cb,
466 /* If walking the configuration was inconclusive, deny access. */
467 if (!authz_access_is_determined(baton.allow,
468 baton.deny, baton.required_access))
476 /*** Validating the authz file. ***/
478 /* Check for errors in GROUP's definition of CFG. The errors
479 * detected are references to non-existent groups and circular
480 * dependencies between groups. If an error is found, return
481 * SVN_ERR_AUTHZ_INVALID_CONFIG. Use POOL for temporary
484 * CHECKED_GROUPS should be an empty (it is used for recursive calls).
487 authz_group_walk(svn_config_t *cfg,
489 apr_hash_t *checked_groups,
493 apr_array_header_t *list;
496 svn_config_get(cfg, &value, "groups", group, NULL);
497 /* Having a non-existent group in the ACL configuration might be the
498 sign of a typo. Refuse to perform authz on uncertain rules. */
500 return svn_error_createf(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
501 "An authz rule refers to group '%s', "
502 "which is undefined",
505 list = svn_cstring_split(value, ",", TRUE, pool);
507 for (i = 0; i < list->nelts; i++)
509 const char *group_user = APR_ARRAY_IDX(list, i, char *);
511 /* If the 'user' is a subgroup, recurse into it. */
512 if (*group_user == '@')
514 /* A circular dependency between groups is a Bad Thing. We
515 don't do authz with invalid ACL files. */
516 if (svn_hash_gets(checked_groups, &group_user[1]))
517 return svn_error_createf(SVN_ERR_AUTHZ_INVALID_CONFIG,
519 "Circular dependency between "
520 "groups '%s' and '%s'",
521 &group_user[1], group);
523 /* Add group to hash of checked groups. */
524 svn_hash_sets(checked_groups, &group_user[1], "");
526 /* Recurse on that group. */
527 SVN_ERR(authz_group_walk(cfg, &group_user[1],
528 checked_groups, pool));
530 /* Remove group from hash of checked groups, so that we don't
531 incorrectly report an error if we see it again as part of
533 svn_hash_sets(checked_groups, &group_user[1], NULL);
535 else if (*group_user == '&')
539 svn_config_get(cfg, &alias, "aliases", &group_user[1], NULL);
540 /* Having a non-existent alias in the ACL configuration might be the
541 sign of a typo. Refuse to perform authz on uncertain rules. */
543 return svn_error_createf(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
544 "An authz rule refers to alias '%s', "
545 "which is undefined",
554 /* Callback to perform some simple sanity checks on an authz rule.
556 * - If RULE_MATCH_STRING references a group or an alias, verify that
557 * the group or alias definition exists.
558 * - If RULE_MATCH_STRING specifies a token (starts with $), verify
559 * that the token name is valid.
560 * - If RULE_MATCH_STRING is using inversion, verify that it isn't
561 * doing it more than once within the one rule, and that it isn't
562 * "~*", as that would never match.
563 * - Check that VALUE part of the rule specifies only allowed rule
564 * flag characters ('r' and 'w').
566 * Return TRUE if the rule has no errors. Use BATON for context and
569 static svn_boolean_t authz_validate_rule(const char *rule_match_string,
575 const char *match = rule_match_string;
576 struct authz_validate_baton *b = baton;
578 /* Make sure the user isn't using double-negatives. */
581 /* Bump the pointer past the inversion for the other checks. */
584 /* Another inversion is a double negative; we can't not stop. */
587 b->err = svn_error_createf(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
588 "Rule '%s' has more than one "
589 "inversion; double negatives are "
595 /* Make sure that the rule isn't "~*", which won't ever match. */
596 if (strcmp(match, "*") == 0)
598 b->err = svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
599 "Authz rules with match string '~*' "
600 "are not allowed, because they never "
606 /* If the rule applies to a group, check its existence. */
609 const char *group = &match[1];
611 svn_config_get(b->config, &val, "groups", group, NULL);
613 /* Having a non-existent group in the ACL configuration might be
614 the sign of a typo. Refuse to perform authz on uncertain
618 b->err = svn_error_createf(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
619 "An authz rule refers to group "
620 "'%s', which is undefined",
626 /* If the rule applies to an alias, check its existence. */
629 const char *alias = &match[1];
631 svn_config_get(b->config, &val, "aliases", alias, NULL);
635 b->err = svn_error_createf(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
636 "An authz rule refers to alias "
637 "'%s', which is undefined",
643 /* If the rule specifies a token, check its validity. */
646 const char *token_name = &match[1];
648 if ((strcmp(token_name, "anonymous") != 0)
649 && (strcmp(token_name, "authenticated") != 0))
651 b->err = svn_error_createf(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
652 "Unrecognized authz token '%s'.",
662 if (*val != 'r' && *val != 'w' && ! svn_ctype_isspace(*val))
664 b->err = svn_error_createf(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
665 "The character '%c' in rule '%s' is not "
666 "allowed in authz rules", *val,
677 /* Callback to check ALIAS's definition for validity. Use
678 BATON for context and error reporting. */
679 static svn_boolean_t authz_validate_alias(const char *alias,
684 /* No checking at the moment, every alias is valid */
689 /* Callback to check GROUP's definition for cyclic dependancies. Use
690 BATON for context and error reporting. */
691 static svn_boolean_t authz_validate_group(const char *group,
696 struct authz_validate_baton *b = baton;
698 b->err = authz_group_walk(b->config, group, apr_hash_make(pool), pool);
706 /* Callback to check the contents of the configuration section given
707 by NAME. Use BATON for context and error reporting. */
708 static svn_boolean_t authz_validate_section(const char *name,
712 struct authz_validate_baton *b = baton;
714 /* Use the group checking callback for the "groups" section... */
715 if (strcmp(name, "groups") == 0)
716 svn_config_enumerate2(b->config, name, authz_validate_group,
718 /* ...and the alias checking callback for "aliases"... */
719 else if (strcmp(name, "aliases") == 0)
720 svn_config_enumerate2(b->config, name, authz_validate_alias,
722 /* ...but for everything else use the rule checking callback. */
725 /* Validate the section's name. Skip the optional REPOS_NAME. */
726 const char *fspath = strchr(name, ':');
731 if (! svn_fspath__is_canonical(fspath))
733 b->err = svn_error_createf(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
734 "Section name '%s' contains non-canonical "
740 svn_config_enumerate2(b->config, name, authz_validate_rule,
751 /* Walk the configuration in AUTHZ looking for any errors. */
753 authz_validate(svn_authz_t *authz, apr_pool_t *pool)
755 struct authz_validate_baton baton = { 0 };
757 baton.err = SVN_NO_ERROR;
758 baton.config = authz->cfg;
760 /* Step through the entire rule file stopping on error. */
761 svn_config_enumerate_sections2(authz->cfg, authz_validate_section,
769 /* Retrieve the file at DIRENT (contained in a repo) then parse it as a config
770 * file placing the result into CFG_P allocated in POOL.
772 * If DIRENT cannot be parsed as a config file then an error is returned. The
773 * contents of CFG_P is then undefined. If MUST_EXIST is TRUE, a missing
774 * authz file is also an error.
776 * SCRATCH_POOL will be used for temporary allocations. */
778 authz_retrieve_config_repo(svn_config_t **cfg_p, const char *dirent,
779 svn_boolean_t must_exist,
780 apr_pool_t *result_pool, apr_pool_t *scratch_pool)
784 const char *repos_root_dirent;
788 svn_revnum_t youngest_rev;
789 svn_node_kind_t node_kind;
790 svn_stream_t *contents;
792 /* Search for a repository in the full path. */
793 repos_root_dirent = svn_repos_find_root_path(dirent, scratch_pool);
794 if (!repos_root_dirent)
795 return svn_error_createf(SVN_ERR_RA_LOCAL_REPOS_NOT_FOUND, NULL,
796 "Unable to find repository at '%s'", dirent);
798 /* Attempt to open a repository at repos_root_dirent. */
799 SVN_ERR(svn_repos_open2(&repos, repos_root_dirent, NULL, scratch_pool));
801 fs_path = &dirent[strlen(repos_root_dirent)];
803 /* Root path is always a directory so no reason to go any further */
804 if (*fs_path == '\0')
805 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
806 "'/' is not a file in repo '%s'",
809 /* We skip some things that are non-important for how we're going to use
810 * this repo connection. We do not set any capabilities since none of
811 * the current ones are important for what we're doing. We also do not
812 * setup the environment that repos hooks would run under since we won't
813 * be triggering any. */
815 /* Get the filesystem. */
816 fs = svn_repos_fs(repos);
818 /* Find HEAD and the revision root */
819 SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, scratch_pool));
820 SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, scratch_pool));
822 SVN_ERR(svn_fs_check_path(&node_kind, root, fs_path, scratch_pool));
823 if (node_kind == svn_node_none)
827 SVN_ERR(svn_config_create2(cfg_p, TRUE, TRUE, result_pool));
832 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
833 "'%s' path not found in repo '%s'", fs_path,
837 else if (node_kind != svn_node_file)
839 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
840 "'%s' is not a file in repo '%s'", fs_path,
844 SVN_ERR(svn_fs_file_contents(&contents, root, fs_path, scratch_pool));
845 err = svn_config_parse(cfg_p, contents, TRUE, TRUE, result_pool);
847 /* Add the URL to the error stack since the parser doesn't have it. */
848 if (err != SVN_NO_ERROR)
849 return svn_error_createf(err->apr_err, err,
850 "Error while parsing config file: '%s' in repo '%s':",
851 fs_path, repos_root_dirent);
856 /* Given a PATH which might be a relative repo URL (^/), an absolute
857 * local repo URL (file://), an absolute path outside of the repo
858 * or a location in the Windows registry.
860 * Retrieve the configuration data that PATH points at and parse it into
861 * CFG_P allocated in POOL.
863 * If PATH cannot be parsed as a config file then an error is returned. The
864 * contents of CFG_P is then undefined. If MUST_EXIST is TRUE, a missing
865 * authz file is also an error.
867 * REPOS_ROOT points at the root of the repos you are
868 * going to apply the authz against, can be NULL if you are sure that you
869 * don't have a repos relative URL in PATH. */
871 authz_retrieve_config(svn_config_t **cfg_p, const char *path,
872 svn_boolean_t must_exist, apr_pool_t *pool)
874 if (svn_path_is_url(path))
878 apr_pool_t *scratch_pool = svn_pool_create(pool);
880 err = svn_uri_get_dirent_from_file_url(&dirent, path, scratch_pool);
882 if (err == SVN_NO_ERROR)
883 err = authz_retrieve_config_repo(cfg_p, dirent, must_exist, pool,
886 /* Close the repos and streams we opened. */
887 svn_pool_destroy(scratch_pool);
893 /* Outside of repo file or Windows registry*/
894 SVN_ERR(svn_config_read3(cfg_p, path, must_exist, TRUE, TRUE, pool));
901 /* Callback to copy (name, value) group into the "groups" section
902 of another configuration. */
904 authz_copy_group(const char *name, const char *value,
905 void *baton, apr_pool_t *pool)
907 svn_config_t *authz_cfg = baton;
909 svn_config_set(authz_cfg, SVN_CONFIG_SECTION_GROUPS, name, value);
914 /* Copy group definitions from GROUPS_CFG to the resulting AUTHZ.
915 * If AUTHZ already contains any group definition, report an error.
916 * Use POOL for temporary allocations. */
918 authz_copy_groups(svn_authz_t *authz, svn_config_t *groups_cfg,
921 /* Easy out: we prohibit local groups in the authz file when global
922 groups are being used. */
923 if (svn_config_has_section(authz->cfg, SVN_CONFIG_SECTION_GROUPS))
925 return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
926 "Authz file cannot contain any groups "
927 "when global groups are being used.");
930 svn_config_enumerate2(groups_cfg, SVN_CONFIG_SECTION_GROUPS,
931 authz_copy_group, authz->cfg, pool);
937 svn_repos__authz_read(svn_authz_t **authz_p, const char *path,
938 const char *groups_path, svn_boolean_t must_exist,
939 svn_boolean_t accept_urls, apr_pool_t *pool)
941 svn_authz_t *authz = apr_palloc(pool, sizeof(*authz));
943 /* Load the authz file */
945 SVN_ERR(authz_retrieve_config(&authz->cfg, path, must_exist, pool));
947 SVN_ERR(svn_config_read3(&authz->cfg, path, must_exist, TRUE, TRUE, pool));
951 svn_config_t *groups_cfg;
954 /* Load the groups file */
956 SVN_ERR(authz_retrieve_config(&groups_cfg, groups_path, must_exist,
959 SVN_ERR(svn_config_read3(&groups_cfg, groups_path, must_exist,
962 /* Copy the groups from groups_cfg into authz. */
963 err = authz_copy_groups(authz, groups_cfg, pool);
965 /* Add the paths to the error stack since the authz_copy_groups
966 routine knows nothing about them. */
967 if (err != SVN_NO_ERROR)
968 return svn_error_createf(err->apr_err, err,
969 "Error reading authz file '%s' with "
970 "groups file '%s':", path, groups_path);
973 /* Make sure there are no errors in the configuration. */
974 SVN_ERR(authz_validate(authz, pool));
982 /*** Public functions. ***/
985 svn_repos_authz_read2(svn_authz_t **authz_p, const char *path,
986 const char *groups_path, svn_boolean_t must_exist,
989 return svn_repos__authz_read(authz_p, path, groups_path, must_exist,
995 svn_repos_authz_parse(svn_authz_t **authz_p, svn_stream_t *stream,
996 svn_stream_t *groups_stream, apr_pool_t *pool)
998 svn_authz_t *authz = apr_palloc(pool, sizeof(*authz));
1000 /* Parse the authz stream */
1001 SVN_ERR(svn_config_parse(&authz->cfg, stream, TRUE, TRUE, pool));
1005 svn_config_t *groups_cfg;
1007 /* Parse the groups stream */
1008 SVN_ERR(svn_config_parse(&groups_cfg, groups_stream, TRUE, TRUE, pool));
1010 SVN_ERR(authz_copy_groups(authz, groups_cfg, pool));
1013 /* Make sure there are no errors in the configuration. */
1014 SVN_ERR(authz_validate(authz, pool));
1017 return SVN_NO_ERROR;
1022 svn_repos_authz_check_access(svn_authz_t *authz, const char *repos_name,
1023 const char *path, const char *user,
1024 svn_repos_authz_access_t required_access,
1025 svn_boolean_t *access_granted,
1028 const char *current_path;
1033 /* If PATH is NULL, check if the user has *any* access. */
1036 *access_granted = authz_get_any_access(authz->cfg, repos_name,
1037 user, required_access, pool);
1038 return SVN_NO_ERROR;
1042 SVN_ERR_ASSERT(path[0] == '/');
1044 /* Determine the granted access for the requested path. */
1045 path = svn_fspath__canonicalize(path, pool);
1046 current_path = path;
1048 while (!authz_get_path_access(authz->cfg, repos_name,
1054 /* Stop if the loop hits the repository root with no
1056 if (current_path[0] == '/' && current_path[1] == '\0')
1058 /* Deny access by default. */
1059 *access_granted = FALSE;
1060 return SVN_NO_ERROR;
1063 /* Work back to the parent path. */
1064 current_path = svn_fspath__dirname(current_path, pool);
1067 /* If the caller requested recursive access, we need to walk through
1068 the entire authz config to see whether any child paths are denied
1069 to the requested user. */
1070 if (*access_granted && (required_access & svn_authz_recursive))
1071 *access_granted = authz_get_tree_access(authz->cfg, repos_name, path,
1072 user, required_access, pool);
1074 return SVN_NO_ERROR;