| Directory: | cvmfs/ |
|---|---|
| File: | cvmfs/authz/authz_fetch.cc |
| Date: | 2026-03-22 02:40:38 |
| Exec | Total | Coverage | |
|---|---|---|---|
| Lines: | 275 | 336 | 81.8% |
| Branches: | 225 | 457 | 49.2% |
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * This file is part of the CernVM File System. | ||
| 3 | */ | ||
| 4 | |||
| 5 | #define __STDC_FORMAT_MACROS | ||
| 6 | #include "authz_fetch.h" | ||
| 7 | |||
| 8 | #include <errno.h> | ||
| 9 | #include <signal.h> | ||
| 10 | #include <sys/wait.h> | ||
| 11 | #include <syslog.h> | ||
| 12 | #include <unistd.h> | ||
| 13 | |||
| 14 | #include <algorithm> | ||
| 15 | #include <cassert> | ||
| 16 | #include <cstring> | ||
| 17 | #include <vector> | ||
| 18 | |||
| 19 | #include "monitor.h" | ||
| 20 | #include "options.h" | ||
| 21 | #include "sanitizer.h" | ||
| 22 | #include "util/capabilities.h" | ||
| 23 | #include "util/concurrency.h" | ||
| 24 | #include "util/logging.h" | ||
| 25 | #include "util/platform.h" | ||
| 26 | #include "util/pointer.h" | ||
| 27 | #include "util/posix.h" | ||
| 28 | #include "util/smalloc.h" | ||
| 29 | #include "util/string.h" | ||
| 30 | |||
| 31 | using namespace std; // NOLINT | ||
| 32 | |||
| 33 | |||
| 34 | const int AuthzExternalFetcher::kMinTtl = 0; | ||
| 35 | const uint32_t AuthzExternalFetcher::kProtocolVersion = 1; | ||
| 36 | |||
| 37 | |||
| 38 | 876 | AuthzExternalFetcher::AuthzExternalFetcher(const string &fqrn, | |
| 39 | const string &progname, | ||
| 40 | const string &search_path, | ||
| 41 | 876 | OptionsManager *options_manager) | |
| 42 | 876 | : fqrn_(fqrn) | |
| 43 |
1/2✓ Branch 1 taken 876 times.
✗ Branch 2 not taken.
|
876 | , progname_(progname) |
| 44 |
1/2✓ Branch 1 taken 876 times.
✗ Branch 2 not taken.
|
876 | , search_path_(search_path) |
| 45 | 876 | , fd_send_(-1) | |
| 46 | 876 | , fd_recv_(-1) | |
| 47 | 876 | , pid_(-1) | |
| 48 | 876 | , fail_state_(false) | |
| 49 | 876 | , options_manager_(options_manager) | |
| 50 |
1/2✓ Branch 3 taken 876 times.
✗ Branch 4 not taken.
|
876 | , next_start_(-1) { |
| 51 | 876 | InitLock(); | |
| 52 | 876 | } | |
| 53 | |||
| 54 | 85 | AuthzExternalFetcher::AuthzExternalFetcher(const string &fqrn, | |
| 55 | int fd_send, | ||
| 56 | 85 | int fd_recv) | |
| 57 | 85 | : fqrn_(fqrn) | |
| 58 | 85 | , fd_send_(fd_send) | |
| 59 | 85 | , fd_recv_(fd_recv) | |
| 60 | 85 | , pid_(-1) | |
| 61 | 85 | , fail_state_(false) | |
| 62 | 85 | , options_manager_(NULL) | |
| 63 |
1/2✓ Branch 3 taken 85 times.
✗ Branch 4 not taken.
|
85 | , next_start_(-1) { |
| 64 | 85 | InitLock(); | |
| 65 | 85 | } | |
| 66 | |||
| 67 | |||
| 68 | 3844 | AuthzExternalFetcher::~AuthzExternalFetcher() { | |
| 69 | 1922 | const int retval = pthread_mutex_destroy(&lock_); | |
| 70 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 961 times.
|
1922 | assert(retval == 0); |
| 71 | |||
| 72 | // Allow helper to gracefully terminate | ||
| 73 |
3/4✓ Branch 0 taken 68 times.
✓ Branch 1 taken 893 times.
✓ Branch 2 taken 68 times.
✗ Branch 3 not taken.
|
1922 | if ((fd_send_ >= 0) && !fail_state_) { |
| 74 | 136 | LogCvmfs(kLogAuthz, kLogDebug, "shutting down authz helper"); | |
| 75 | 408 | Send(string("{\"cvmfs_authz_v1\":{") + "\"msgid\":" | |
| 76 | 544 | + StringifyInt(kAuthzMsgQuit) + "," + "\"revision\":0}}"); | |
| 77 | } | ||
| 78 | |||
| 79 | 1922 | ReapHelper(); | |
| 80 | 3844 | } | |
| 81 | |||
| 82 | 1318 | void AuthzExternalFetcher::ReapHelper() { | |
| 83 | // If we are reaping the helper, we don't try to shut it down again. | ||
| 84 | |||
| 85 |
2/2✓ Branch 0 taken 136 times.
✓ Branch 1 taken 1182 times.
|
1318 | if (fd_send_ >= 0) |
| 86 | 136 | close(fd_send_); | |
| 87 | 1318 | fd_send_ = -1; | |
| 88 |
2/2✓ Branch 0 taken 136 times.
✓ Branch 1 taken 1182 times.
|
1318 | if (fd_recv_ >= 0) |
| 89 | 136 | close(fd_recv_); | |
| 90 | 1318 | fd_recv_ = -1; | |
| 91 | |||
| 92 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 1267 times.
|
1318 | if (pid_ > 0) { |
| 93 | int retval; | ||
| 94 | 51 | const uint64_t now = platform_monotonic_time(); | |
| 95 | int statloc; | ||
| 96 | do { | ||
| 97 |
1/2✓ Branch 1 taken 215352328 times.
✗ Branch 2 not taken.
|
215352328 | retval = waitpid(pid_, &statloc, WNOHANG); |
| 98 |
2/2✓ Branch 1 taken 17 times.
✓ Branch 2 taken 215352311 times.
|
215352328 | if (platform_monotonic_time() > (now + kChildTimeout)) { |
| 99 |
1/2✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
|
17 | LogCvmfs(kLogAuthz, kLogSyslogWarn | kLogDebug, |
| 100 | "authz helper %s unresponsive, killing", progname_.c_str()); | ||
| 101 | 17 | retval = kill(pid_, SIGKILL); | |
| 102 |
1/2✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
|
17 | if (retval == 0) { |
| 103 | // Pick up client return status | ||
| 104 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | (void)waitpid(pid_, &statloc, 0); |
| 105 | } else { | ||
| 106 | // Process might have been terminated just before the kill() call | ||
| 107 | ✗ | (void)waitpid(pid_, &statloc, WNOHANG); | |
| 108 | } | ||
| 109 | 17 | break; | |
| 110 | } | ||
| 111 |
2/2✓ Branch 0 taken 215352277 times.
✓ Branch 1 taken 34 times.
|
215352311 | } while (retval == 0); |
| 112 | 51 | pid_ = -1; | |
| 113 | } | ||
| 114 | 1318 | } | |
| 115 | |||
| 116 | |||
| 117 | 357 | void AuthzExternalFetcher::EnterFailState() { | |
| 118 | 357 | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, | |
| 119 | "authz helper %s enters fail state, no more authorization", | ||
| 120 | progname_.c_str()); | ||
| 121 | |||
| 122 | 357 | ReapHelper(); | |
| 123 | 357 | next_start_ = platform_monotonic_time() + kChildTimeout; | |
| 124 | 357 | fail_state_ = true; | |
| 125 | 357 | } | |
| 126 | |||
| 127 | |||
| 128 | /** | ||
| 129 | * Uses execve to start progname_. The started program has stdin and stdout | ||
| 130 | * connected to fd_send_ and fd_recv_ and the CVMFS_... environment variables | ||
| 131 | * set. Special care must be taken when we call fork here in an unknown state | ||
| 132 | * of the client. Therefore we can't use ManagedExec (we can't use malloc). | ||
| 133 | * | ||
| 134 | * A failed execve is not caught by this routine. It will be caught in the | ||
| 135 | * next step, when mother and child start talking. | ||
| 136 | */ | ||
| 137 | 68 | void AuthzExternalFetcher::ExecHelper() { | |
| 138 | int pipe_send[2]; | ||
| 139 | int pipe_recv[2]; | ||
| 140 |
1/2✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
|
68 | MakePipe(pipe_send); |
| 141 |
1/2✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
|
68 | MakePipe(pipe_recv); |
| 142 | 68 | char *argv0 = strdupa(progname_.c_str()); | |
| 143 | 68 | char *argv[] = {argv0, NULL}; | |
| 144 | |||
| 145 | 68 | const bool strip_prefix = true; | |
| 146 | 68 | vector<string> authz_env = options_manager_->GetEnvironmentSubset( | |
| 147 |
2/4✓ Branch 2 taken 68 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 68 times.
✗ Branch 6 not taken.
|
136 | "CVMFS_AUTHZ_", strip_prefix); |
| 148 | 68 | vector<char *> envp; | |
| 149 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 68 times.
|
68 | for (unsigned i = 0; i < authz_env.size(); ++i) |
| 150 | ✗ | envp.push_back(strdupa(authz_env[i].c_str())); | |
| 151 |
1/2✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
|
68 | envp.push_back(strdupa("CVMFS_AUTHZ_HELPER=yes")); |
| 152 |
1/2✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
|
68 | envp.push_back(NULL); |
| 153 | |||
| 154 | #ifdef __APPLE__ | ||
| 155 | int max_fd = sysconf(_SC_OPEN_MAX); | ||
| 156 | assert(max_fd > 0); | ||
| 157 | #else | ||
| 158 | 68 | std::vector<int> open_fds; | |
| 159 |
1/2✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
|
68 | DIR *dirp = opendir("/proc/self/fd"); |
| 160 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
|
68 | assert(dirp); |
| 161 | platform_dirent64 *dirent; | ||
| 162 |
3/4✓ Branch 1 taken 2108 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2040 times.
✓ Branch 4 taken 68 times.
|
2108 | while ((dirent = platform_readdir(dirp))) { |
| 163 |
1/2✓ Branch 2 taken 2040 times.
✗ Branch 3 not taken.
|
2040 | const std::string name(dirent->d_name); |
| 164 | uint64_t name_uint64; | ||
| 165 | // Make sure the dir name is digits only (skips ".", ".." and similar). | ||
| 166 |
3/4✓ Branch 1 taken 2040 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 136 times.
✓ Branch 4 taken 1904 times.
|
2040 | if (!String2Uint64Parse(name, &name_uint64)) |
| 167 | 136 | continue; | |
| 168 |
2/2✓ Branch 0 taken 136 times.
✓ Branch 1 taken 1768 times.
|
1904 | if (name_uint64 < 2) |
| 169 | 136 | continue; | |
| 170 |
1/2✓ Branch 1 taken 1768 times.
✗ Branch 2 not taken.
|
1768 | open_fds.push_back(static_cast<int>(name_uint64)); |
| 171 |
2/2✓ Branch 1 taken 1768 times.
✓ Branch 2 taken 272 times.
|
2040 | } |
| 172 |
1/2✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
|
68 | closedir(dirp); |
| 173 | #endif | ||
| 174 |
1/2✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
|
68 | LogCvmfs(kLogAuthz, kLogDebug | kLogSyslog, "starting authz helper %s", |
| 175 | argv0); | ||
| 176 | |||
| 177 | 68 | const pid_t pid = fork(); | |
| 178 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 68 times.
|
71 | if (pid == 0) { |
| 179 | // Child process, close file descriptors and run the helper | ||
| 180 | 3 | int retval = dup2(pipe_send[0], 0); | |
| 181 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | assert(retval == 0); |
| 182 | 3 | retval = dup2(pipe_recv[1], 1); | |
| 183 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | assert(retval == 1); |
| 184 | #ifdef __APPLE__ | ||
| 185 | for (int fd = 2; fd < max_fd; fd++) | ||
| 186 | close(fd); | ||
| 187 | #else | ||
| 188 |
2/2✓ Branch 1 taken 78 times.
✓ Branch 2 taken 3 times.
|
81 | for (unsigned i = 0; i < open_fds.size(); ++i) |
| 189 |
1/2✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
|
78 | close(open_fds[i]); |
| 190 | #endif | ||
| 191 | |||
| 192 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 3 times.
|
42 | for (size_t i = 0; i < sizeof(Watchdog::g_suppressed_signals) / sizeof(int); |
| 193 | i++) { | ||
| 194 | struct sigaction signal_handler; | ||
| 195 | 39 | signal_handler.sa_handler = SIG_DFL; | |
| 196 | 39 | sigaction(Watchdog::g_suppressed_signals[i], &signal_handler, NULL); | |
| 197 | } | ||
| 198 | |||
| 199 |
4/8✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
|
3 | if ((getuid() != 0) && SetuidCapabilityPermitted()) { |
| 200 | // The helper expects traditional real uid root credentials | ||
| 201 | ✗ | const uid_t uid = geteuid(); | |
| 202 | ✗ | const gid_t gid = getegid(); | |
| 203 | ✗ | if (!SwitchCredentials(0, 0, false) || | |
| 204 | ✗ | !SwitchCredentials(uid, gid, true)) { | |
| 205 | ✗ | syslog(LOG_USER | LOG_ERR, | |
| 206 | "failed to switch back to real uid 0 for authz helper"); | ||
| 207 | ✗ | _exit(1); | |
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | 3 | execve(argv0, argv, &envp[0]); | |
| 212 | 3 | syslog(LOG_USER | LOG_ERR, "failed to start authz helper %s (%d)", argv0, | |
| 213 |
0/2✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
3 | errno); |
| 214 | ✗ | _exit(1); | |
| 215 | } | ||
| 216 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
|
68 | assert(pid > 0); |
| 217 |
1/2✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
|
68 | close(pipe_send[0]); |
| 218 |
1/2✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
|
68 | close(pipe_recv[1]); |
| 219 | |||
| 220 | // Don't receive a signal if the helper terminates | ||
| 221 | 68 | signal(SIGPIPE, SIG_IGN); | |
| 222 | 68 | pid_ = pid; | |
| 223 | 68 | fd_send_ = pipe_send[1]; | |
| 224 | 68 | fd_recv_ = pipe_recv[0]; | |
| 225 | 68 | } | |
| 226 | |||
| 227 | |||
| 228 | ✗ | AuthzStatus AuthzExternalFetcher::CheckHelper( | |
| 229 | const std::string &membership) | ||
| 230 | { | ||
| 231 | ✗ | if ((fd_send_ < 0) && (membership != "")) { | |
| 232 | ✗ | if (progname_.empty()) | |
| 233 | ✗ | progname_ = FindHelper(membership); | |
| 234 | ✗ | ExecHelper(); | |
| 235 | ✗ | if (!Handshake()) | |
| 236 | ✗ | return kAuthzNoHelper; | |
| 237 | } | ||
| 238 | ✗ | return kAuthzOk; | |
| 239 | } | ||
| 240 | |||
| 241 | |||
| 242 | 17 | AuthzStatus AuthzExternalFetcher::Fetch( | |
| 243 | const QueryInfo &query_info, | ||
| 244 | AuthzToken *authz_token, | ||
| 245 | unsigned *ttl) | ||
| 246 | { | ||
| 247 | 17 | *ttl = kDefaultTtl; | |
| 248 | |||
| 249 | 17 | const MutexLockGuard lock_guard(lock_); | |
| 250 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | if (fail_state_) { |
| 251 | ✗ | const uint64_t now = platform_monotonic_time(); | |
| 252 | ✗ | if (now > next_start_) { | |
| 253 | ✗ | fail_state_ = false; | |
| 254 | } else { | ||
| 255 | ✗ | return kAuthzNoHelper; | |
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | bool retval; | ||
| 260 | |||
| 261 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | if (fd_send_ < 0) { |
| 262 | ✗ | return kAuthzNoHelper; | |
| 263 | } | ||
| 264 |
2/4✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
|
17 | assert((fd_send_ >= 0) && (fd_recv_ >= 0)); |
| 265 | |||
| 266 | 17 | string authz_schema; | |
| 267 | 17 | string pure_membership; | |
| 268 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | StripAuthzSchema(query_info.membership, &authz_schema, &pure_membership); |
| 269 |
2/4✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 17 times.
✗ Branch 6 not taken.
|
34 | string json_msg = string("{\"cvmfs_authz_v1\":{") + "\"msgid\":" |
| 270 |
4/8✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17 times.
✗ Branch 11 not taken.
|
68 | + StringifyInt(kAuthzMsgVerify) + "," + "\"revision\":0," |
| 271 |
4/8✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17 times.
✗ Branch 11 not taken.
|
68 | + "\"uid\":" + StringifyInt(query_info.uid) + "," |
| 272 |
4/8✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17 times.
✗ Branch 11 not taken.
|
68 | + "\"gid\":" + StringifyInt(query_info.gid) + "," |
| 273 |
4/8✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17 times.
✗ Branch 11 not taken.
|
68 | + "\"pid\":" + StringifyInt(query_info.pid) + "," |
| 274 |
4/8✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17 times.
✗ Branch 11 not taken.
|
51 | + "\"membership\":\"" + Base64(pure_membership) + "\"}}"; |
| 275 |
4/8✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 17 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 17 times.
✗ Branch 9 not taken.
|
17 | retval = Send(json_msg) && Recv(&json_msg); |
| 276 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | if (!retval) |
| 277 | ✗ | return kAuthzNoHelper; | |
| 278 | 17 | AuthzExternalMsg binary_msg; | |
| 279 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | retval = ParseMsg(json_msg, kAuthzMsgPermit, &binary_msg); |
| 280 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | if (!retval) |
| 281 | ✗ | return kAuthzNoHelper; | |
| 282 | |||
| 283 | 17 | *ttl = binary_msg.permit.ttl; | |
| 284 |
1/2✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
|
17 | if (binary_msg.permit.status == kAuthzOk) { |
| 285 | 17 | *authz_token = binary_msg.permit.token; | |
| 286 | 17 | LogCvmfs(kLogAuthz, kLogDebug, "got token of type %d and size %u", | |
| 287 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | binary_msg.permit.token.type, binary_msg.permit.token.size); |
| 288 | } | ||
| 289 | |||
| 290 | 17 | return binary_msg.permit.status; | |
| 291 | 17 | } | |
| 292 | |||
| 293 | |||
| 294 | ✗ | string AuthzExternalFetcher::FindHelper(const string &membership) { | |
| 295 | ✗ | string authz_schema; | |
| 296 | ✗ | string pure_membership; | |
| 297 | ✗ | StripAuthzSchema(membership, &authz_schema, &pure_membership); | |
| 298 | ✗ | const sanitizer::AuthzSchemaSanitizer sanitizer; | |
| 299 | ✗ | if (!sanitizer.IsValid(authz_schema)) { | |
| 300 | ✗ | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, "invalid authz schema: %s", | |
| 301 | authz_schema.c_str()); | ||
| 302 | ✗ | return ""; | |
| 303 | } | ||
| 304 | |||
| 305 | ✗ | string exe_path = search_path_ + "/cvmfs_" + authz_schema + "_helper"; | |
| 306 | ✗ | if (!FileExists(exe_path)) { | |
| 307 | ✗ | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, "authz helper %s missing", | |
| 308 | exe_path.c_str()); | ||
| 309 | } | ||
| 310 | ✗ | return exe_path; | |
| 311 | } | ||
| 312 | |||
| 313 | |||
| 314 | /** | ||
| 315 | * Establish communication link with a forked authz helper. | ||
| 316 | */ | ||
| 317 | 34 | bool AuthzExternalFetcher::Handshake() { | |
| 318 |
1/3✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
34 | const string debug_log = GetLogDebugFile(); |
| 319 | 34 | string json_debug_log; | |
| 320 |
2/4✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 34 times.
|
34 | if (debug_log != "") |
| 321 | ✗ | json_debug_log = ",\"debug_log\":\"" + debug_log + "\""; | |
| 322 |
2/4✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 34 times.
✗ Branch 6 not taken.
|
68 | string json_msg = string("{") + "\"cvmfs_authz_v1\":{" |
| 323 |
4/8✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 34 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 34 times.
✗ Branch 11 not taken.
|
102 | + "\"msgid\":" + StringifyInt(0) + "," + "\"revision\":0," |
| 324 |
5/10✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 34 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 34 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 34 times.
✗ Branch 14 not taken.
|
102 | + "\"fqrn\":\"" + fqrn_ + "\"," + "\"syslog_facility\":" |
| 325 |
4/8✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 34 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 34 times.
✗ Branch 11 not taken.
|
136 | + StringifyInt(GetLogSyslogFacility()) + "," |
| 326 |
4/8✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 34 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 34 times.
✗ Branch 11 not taken.
|
102 | + "\"syslog_level\":" + StringifyInt(GetLogSyslogLevel()) |
| 327 |
2/4✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
|
68 | + json_debug_log + "}}"; |
| 328 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | bool retval = Send(json_msg); |
| 329 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 17 times.
|
34 | if (!retval) |
| 330 | 17 | return false; | |
| 331 | |||
| 332 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | retval = Recv(&json_msg); |
| 333 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | if (!retval) |
| 334 | ✗ | return false; | |
| 335 | 17 | AuthzExternalMsg binary_msg; | |
| 336 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | retval = ParseMsg(json_msg, kAuthzMsgReady, &binary_msg); |
| 337 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | if (!retval) |
| 338 | ✗ | return false; | |
| 339 | |||
| 340 | 17 | return true; | |
| 341 | 34 | } | |
| 342 | |||
| 343 | |||
| 344 | 961 | void AuthzExternalFetcher::InitLock() { | |
| 345 | 961 | const int retval = pthread_mutex_init(&lock_, NULL); | |
| 346 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 961 times.
|
961 | assert(retval == 0); |
| 347 | 961 | } | |
| 348 | |||
| 349 | |||
| 350 | 170 | bool AuthzExternalFetcher::Send(const string &msg) { | |
| 351 | // Line format: 4 byte protocol version, 4 byte length, message | ||
| 352 | struct { | ||
| 353 | uint32_t version; | ||
| 354 | uint32_t length; | ||
| 355 | } header; | ||
| 356 | 170 | header.version = kProtocolVersion; | |
| 357 | 170 | header.length = msg.length(); | |
| 358 | 170 | const unsigned raw_length = sizeof(header) + msg.length(); | |
| 359 | unsigned char *raw_msg = reinterpret_cast<unsigned char *>( | ||
| 360 | 170 | alloca(raw_length)); | |
| 361 | 170 | memcpy(raw_msg, &header, sizeof(header)); | |
| 362 | 170 | memcpy(raw_msg + sizeof(header), msg.data(), header.length); | |
| 363 | |||
| 364 |
1/2✓ Branch 1 taken 170 times.
✗ Branch 2 not taken.
|
170 | const bool retval = SafeWrite(fd_send_, raw_msg, raw_length); |
| 365 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 136 times.
|
170 | if (!retval) |
| 366 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | EnterFailState(); |
| 367 | 170 | return retval; | |
| 368 | } | ||
| 369 | |||
| 370 | |||
| 371 | /** | ||
| 372 | * We want to see valid JSON in the form | ||
| 373 | * { "cvmfs_authz_v1" : { | ||
| 374 | * "msgid": | ||
| 375 | * "revision": | ||
| 376 | * ... | ||
| 377 | * } | ||
| 378 | * ... | ||
| 379 | * } | ||
| 380 | * | ||
| 381 | * The contents of "cvmfs_authz_v1" depends on the msgid. Additional fields | ||
| 382 | * are ignored. The protocol revision should indicate changes in the fields. | ||
| 383 | */ | ||
| 384 | 323 | bool AuthzExternalFetcher::ParseMsg(const std::string &json_msg, | |
| 385 | const AuthzExternalMsgIds expected_msgid, | ||
| 386 | AuthzExternalMsg *binary_msg) { | ||
| 387 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 323 times.
|
323 | assert(binary_msg != NULL); |
| 388 | |||
| 389 |
2/4✓ Branch 1 taken 323 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 323 times.
✗ Branch 5 not taken.
|
323 | const UniquePtr<JsonDocument> json_document(JsonDocument::Create(json_msg)); |
| 390 |
2/2✓ Branch 1 taken 34 times.
✓ Branch 2 taken 289 times.
|
323 | if (!json_document.IsValid()) { |
| 391 |
1/2✓ Branch 3 taken 34 times.
✗ Branch 4 not taken.
|
34 | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
| 392 | "invalid json from authz helper %s: %s", progname_.c_str(), | ||
| 393 | json_msg.c_str()); | ||
| 394 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | EnterFailState(); |
| 395 | 34 | return false; | |
| 396 | } | ||
| 397 | |||
| 398 |
2/4✓ Branch 2 taken 289 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 289 times.
✗ Branch 8 not taken.
|
289 | const JSON *json_authz = JsonDocument::SearchInObject( |
| 399 | json_document->root(), "cvmfs_authz_v1", JSON_OBJECT); | ||
| 400 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 272 times.
|
289 | if (json_authz == NULL) { |
| 401 |
1/2✓ Branch 3 taken 17 times.
✗ Branch 4 not taken.
|
17 | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
| 402 | "\"cvmfs_authz_v1\" not found in json from authz helper %s: %s", | ||
| 403 | progname_.c_str(), json_msg.c_str()); | ||
| 404 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | EnterFailState(); |
| 405 | 17 | return false; | |
| 406 | } | ||
| 407 | |||
| 408 |
1/2✓ Branch 1 taken 272 times.
✗ Branch 2 not taken.
|
272 | if (!ParseMsgId(json_authz, binary_msg) |
| 409 |
6/6✓ Branch 0 taken 204 times.
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 170 times.
✓ Branch 4 taken 102 times.
✓ Branch 5 taken 170 times.
|
272 | || (binary_msg->msgid != expected_msgid)) { |
| 410 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
102 | EnterFailState(); |
| 411 | 102 | return false; | |
| 412 | } | ||
| 413 |
3/4✓ Branch 1 taken 170 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
✓ Branch 4 taken 153 times.
|
170 | if (!ParseRevision(json_authz, binary_msg)) { |
| 414 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | EnterFailState(); |
| 415 | 17 | return false; | |
| 416 | } | ||
| 417 |
2/2✓ Branch 0 taken 102 times.
✓ Branch 1 taken 51 times.
|
153 | if (binary_msg->msgid == kAuthzMsgPermit) { |
| 418 |
3/4✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
✓ Branch 4 taken 85 times.
|
102 | if (!ParsePermit(json_authz, binary_msg)) { |
| 419 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | EnterFailState(); |
| 420 | 17 | return false; | |
| 421 | } | ||
| 422 | } | ||
| 423 | 136 | return true; | |
| 424 | 323 | } | |
| 425 | |||
| 426 | |||
| 427 | 272 | bool AuthzExternalFetcher::ParseMsgId(const JSON *json_authz, | |
| 428 | AuthzExternalMsg *binary_msg) { | ||
| 429 |
2/4✓ Branch 2 taken 272 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 272 times.
✗ Branch 6 not taken.
|
272 | const JSON *json_msgid = JsonDocument::SearchInObject(json_authz, "msgid", |
| 430 | JSON_INT); | ||
| 431 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 238 times.
|
272 | if (json_msgid == NULL) { |
| 432 | 34 | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, | |
| 433 | "\"msgid\" not found in json from authz helper %s", | ||
| 434 | progname_.c_str()); | ||
| 435 | 34 | EnterFailState(); | |
| 436 | 34 | return false; | |
| 437 | } | ||
| 438 | |||
| 439 | 238 | if ((json_msgid->get<int>() < 0) | |
| 440 |
6/6✓ Branch 0 taken 221 times.
✓ Branch 1 taken 17 times.
✓ Branch 3 taken 17 times.
✓ Branch 4 taken 204 times.
✓ Branch 5 taken 34 times.
✓ Branch 6 taken 204 times.
|
238 | || (json_msgid->get<int>() >= kAuthzMsgInvalid)) { |
| 441 | 34 | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, | |
| 442 | "invalid \"msgid\" in json from authz helper %s: %d", | ||
| 443 | progname_.c_str(), json_msgid->get<int>()); | ||
| 444 | 34 | EnterFailState(); | |
| 445 | 34 | return false; | |
| 446 | } | ||
| 447 | |||
| 448 | 204 | binary_msg->msgid = static_cast<AuthzExternalMsgIds>(json_msgid->get<int>()); | |
| 449 | 204 | return true; | |
| 450 | } | ||
| 451 | |||
| 452 | |||
| 453 | /** | ||
| 454 | * A permit must contain the authorization status. Optionally it can come with | ||
| 455 | * a "time to live" of the answer and a token (e.g. X.509 proxy certificate). | ||
| 456 | */ | ||
| 457 | 102 | bool AuthzExternalFetcher::ParsePermit(const JSON *json_authz, | |
| 458 | AuthzExternalMsg *binary_msg) { | ||
| 459 |
2/4✓ Branch 2 taken 102 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 102 times.
✗ Branch 6 not taken.
|
102 | const JSON *json_status = JsonDocument::SearchInObject(json_authz, "status", |
| 460 | JSON_INT); | ||
| 461 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 85 times.
|
102 | if (json_status == NULL) { |
| 462 | 17 | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, | |
| 463 | "\"status\" not found in json from authz helper %s", | ||
| 464 | progname_.c_str()); | ||
| 465 | 17 | EnterFailState(); | |
| 466 | 17 | return false; | |
| 467 | } | ||
| 468 | 85 | if ((json_status->get<int>() < 0) | |
| 469 |
3/6✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 85 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 85 times.
|
85 | || (json_status->get<int>() > kAuthzUnknown)) { |
| 470 | ✗ | binary_msg->permit.status = kAuthzUnknown; | |
| 471 | } else { | ||
| 472 | 85 | binary_msg->permit.status = static_cast<AuthzStatus>( | |
| 473 | 85 | json_status->get<int>()); | |
| 474 | } | ||
| 475 | |||
| 476 |
2/4✓ Branch 2 taken 85 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 85 times.
✗ Branch 6 not taken.
|
85 | const JSON *json_ttl = JsonDocument::SearchInObject(json_authz, "ttl", |
| 477 | JSON_INT); | ||
| 478 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 68 times.
|
85 | if (json_ttl == NULL) { |
| 479 | 17 | LogCvmfs(kLogAuthz, kLogDebug, "no ttl, using default"); | |
| 480 | 17 | binary_msg->permit.ttl = kDefaultTtl; | |
| 481 | } else { | ||
| 482 | 68 | binary_msg->permit.ttl = std::max(kMinTtl, json_ttl->get<int>()); | |
| 483 | } | ||
| 484 | |||
| 485 |
2/4✓ Branch 2 taken 85 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 85 times.
✗ Branch 6 not taken.
|
85 | const JSON *json_token = JsonDocument::SearchInObject( |
| 486 | json_authz, "x509_proxy", JSON_STRING); | ||
| 487 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 51 times.
|
85 | if (json_token != NULL) { |
| 488 | 34 | binary_msg->permit.token.type = kTokenX509; | |
| 489 | 34 | string token_binary; | |
| 490 |
2/4✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 34 times.
✗ Branch 5 not taken.
|
34 | const bool valid_base64 = Debase64(json_token->get<string>(), |
| 491 | &token_binary); | ||
| 492 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
|
34 | if (!valid_base64) { |
| 493 | ✗ | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, | |
| 494 | "invalid Base64 in 'x509_proxy' from authz helper %s", | ||
| 495 | progname_.c_str()); | ||
| 496 | ✗ | EnterFailState(); | |
| 497 | ✗ | return false; | |
| 498 | } | ||
| 499 | 34 | const unsigned size = token_binary.size(); | |
| 500 | 34 | binary_msg->permit.token.size = size; | |
| 501 |
1/2✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
|
34 | if (size > 0) { |
| 502 | // The token is passed to the AuthzSessionManager, which takes care of | ||
| 503 | // freeing the memory | ||
| 504 | 34 | binary_msg->permit.token.data = smalloc(size); | |
| 505 | 34 | memcpy(binary_msg->permit.token.data, token_binary.data(), size); | |
| 506 | } | ||
| 507 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | } |
| 508 | |||
| 509 |
2/4✓ Branch 2 taken 85 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 85 times.
✗ Branch 6 not taken.
|
85 | json_token = JsonDocument::SearchInObject(json_authz, "bearer_token", |
| 510 | JSON_STRING); | ||
| 511 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
|
85 | if (json_token != NULL) { |
| 512 | ✗ | binary_msg->permit.token.type = kTokenBearer; | |
| 513 | ✗ | const unsigned size = json_token->get<string>().size(); | |
| 514 | ✗ | binary_msg->permit.token.size = size; | |
| 515 | ✗ | if (size > 0) { | |
| 516 | // The token is passed to the AuthzSessionManager, which takes care of | ||
| 517 | // freeing the memory | ||
| 518 | ✗ | binary_msg->permit.token.data = smalloc(size); | |
| 519 | ✗ | memcpy(binary_msg->permit.token.data, json_token->get<string>().data(), | |
| 520 | size); | ||
| 521 | |||
| 522 | ✗ | LogCvmfs(kLogAuthz, kLogDebug, | |
| 523 | "Got a bearer_token from authz_helper. " | ||
| 524 | "Setting token type to kTokenBearer"); | ||
| 525 | } else { | ||
| 526 | // We got a bearer_token, but a size 0 (or negative?) string | ||
| 527 | ✗ | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, | |
| 528 | "bearer_token was in returned JSON from Authz helper," | ||
| 529 | " but of size 0 from authz helper %s", | ||
| 530 | progname_.c_str()); | ||
| 531 | } | ||
| 532 | } | ||
| 533 | |||
| 534 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 51 times.
|
85 | if (binary_msg->permit.token.type == kTokenUnknown) { |
| 535 | // No auth token returned, so authz should do... what exactly? | ||
| 536 | // Log error message | ||
| 537 | 34 | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, | |
| 538 | "No auth token found in returned JSON from Authz helper %s", | ||
| 539 | progname_.c_str()); | ||
| 540 | } | ||
| 541 | |||
| 542 | 85 | return true; | |
| 543 | } | ||
| 544 | |||
| 545 | |||
| 546 | 170 | bool AuthzExternalFetcher::ParseRevision(const JSON *json_authz, | |
| 547 | AuthzExternalMsg *binary_msg) { | ||
| 548 |
2/4✓ Branch 2 taken 170 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 170 times.
✗ Branch 6 not taken.
|
170 | const JSON *json_revision = JsonDocument::SearchInObject( |
| 549 | json_authz, "revision", JSON_INT); | ||
| 550 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 170 times.
|
170 | if (json_revision == NULL) { |
| 551 | ✗ | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, | |
| 552 | "\"revision\" not found in json from authz helper %s", | ||
| 553 | progname_.c_str()); | ||
| 554 | ✗ | EnterFailState(); | |
| 555 | ✗ | return false; | |
| 556 | } | ||
| 557 | |||
| 558 |
2/2✓ Branch 1 taken 17 times.
✓ Branch 2 taken 153 times.
|
170 | if (json_revision->get<int>() < 0) { |
| 559 | 17 | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, | |
| 560 | "invalid \"revision\" in json from authz helper %s: %d", | ||
| 561 | progname_.c_str(), json_revision->get<int>()); | ||
| 562 | 17 | EnterFailState(); | |
| 563 | 17 | return false; | |
| 564 | } | ||
| 565 | |||
| 566 | 153 | binary_msg->protocol_revision = json_revision->get<int>(); | |
| 567 | 153 | return true; | |
| 568 | } | ||
| 569 | |||
| 570 | |||
| 571 | 68 | bool AuthzExternalFetcher::Recv(string *msg) { | |
| 572 | uint32_t version; | ||
| 573 |
1/2✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
|
68 | ssize_t retval = SafeRead(fd_recv_, &version, sizeof(version)); |
| 574 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 51 times.
|
68 | if (retval != static_cast<int>(sizeof(version))) { |
| 575 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | EnterFailState(); |
| 576 | 17 | return false; | |
| 577 | } | ||
| 578 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 34 times.
|
51 | if (version != kProtocolVersion) { |
| 579 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, |
| 580 | "authz helper uses unknown protocol version %u", version); | ||
| 581 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | EnterFailState(); |
| 582 | 17 | return false; | |
| 583 | } | ||
| 584 | |||
| 585 | uint32_t length; | ||
| 586 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | retval = SafeRead(fd_recv_, &length, sizeof(length)); |
| 587 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
|
34 | if (retval != static_cast<int>(sizeof(length))) { |
| 588 | ✗ | EnterFailState(); | |
| 589 | ✗ | return false; | |
| 590 | } | ||
| 591 | |||
| 592 | 34 | msg->clear(); | |
| 593 | char buf[kPageSize]; | ||
| 594 | 34 | unsigned nbytes = 0; | |
| 595 |
2/2✓ Branch 0 taken 68 times.
✓ Branch 1 taken 34 times.
|
102 | while (nbytes < length) { |
| 596 | 68 | const unsigned remaining = length - nbytes; | |
| 597 |
1/2✓ Branch 2 taken 68 times.
✗ Branch 3 not taken.
|
68 | retval = SafeRead(fd_recv_, buf, std::min(kPageSize, remaining)); |
| 598 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
|
68 | if (retval < 0) { |
| 599 | ✗ | LogCvmfs(kLogAuthz, kLogSyslogErr | kLogDebug, | |
| 600 | "read failure from authz helper %s", progname_.c_str()); | ||
| 601 | ✗ | EnterFailState(); | |
| 602 | ✗ | return false; | |
| 603 | } | ||
| 604 | 68 | nbytes += retval; | |
| 605 |
1/2✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
|
68 | msg->append(buf, retval); |
| 606 | } | ||
| 607 | |||
| 608 | 34 | return true; | |
| 609 | } | ||
| 610 | |||
| 611 | |||
| 612 | 17 | void AuthzExternalFetcher::StripAuthzSchema(const string &membership, | |
| 613 | string *authz_schema, | ||
| 614 | string *pure_membership) { | ||
| 615 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | vector<string> components = SplitString(membership, '%'); |
| 616 |
1/2✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
|
17 | *authz_schema = components[0]; |
| 617 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | if (components.size() < 2) { |
| 618 |
1/2✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
|
17 | LogCvmfs(kLogAuthz, kLogDebug, "invalid membership: %s", |
| 619 | membership.c_str()); | ||
| 620 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | *pure_membership = ""; |
| 621 | 17 | return; | |
| 622 | } | ||
| 623 | |||
| 624 | ✗ | components.erase(components.begin()); | |
| 625 | ✗ | *pure_membership = JoinStrings(components, "%"); | |
| 626 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
|
17 | } |
| 627 |