CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
acl.cc
Go to the documentation of this file.
1 
5 #include "acl.h"
6 
7 #include <cassert>
8 #include <cstring>
9 #include <vector>
10 #include <algorithm>
11 
12 #include <string.h>
13 
14 #include "util/posix.h"
15 
16 using namespace std; // NOLINT
17 
18 #ifdef COMPARE_TO_LIBACL
19 #include "acl/libacl.h"
20 #else // COMPARE_TO_LIBACL
21 
22 // ACL permission bits
23 #define ACL_READ (0x04)
24 #define ACL_WRITE (0x02)
25 #define ACL_EXECUTE (0x01)
26 
27 // ACL tag types
28 #define ACL_UNDEFINED_TAG (0x00)
29 #define ACL_USER_OBJ (0x01)
30 #define ACL_USER (0x02)
31 #define ACL_GROUP_OBJ (0x04)
32 #define ACL_GROUP (0x08)
33 #define ACL_MASK (0x10)
34 #define ACL_OTHER (0x20)
35 
36 // ACL qualifier constants
37 #define ACL_UNDEFINED_ID ((id_t) - 1)
38 
39 #endif // COMPARE_TO_LIBACL
40 
41 #define ACL_EA_VERSION 0x0002
42 
43 // ACL data structures
44 struct acl_ea_entry {
45  u_int16_t e_tag;
46  u_int16_t e_perm;
47  u_int32_t e_id;
48 
49  // implements sorting compatible with libacl
50  bool operator<(const acl_ea_entry& other) const {
51  if (e_tag != other.e_tag) {
52  return e_tag < other.e_tag;
53  }
54  return e_id < other.e_id;
55  }
56 };
57 
58 struct acl_ea_header {
59  u_int32_t a_version;
60  acl_ea_entry a_entries[0];
61 };
62 
63 static int acl_from_text_to_string_entries(const string &acl_string, vector<string> &string_entries)
64 {
65  std::size_t entry_pos = 0;
66  while (entry_pos != string::npos) {
67  size_t entry_length;
68  size_t next_pos;
69  size_t const sep_pos = acl_string.find_first_of(",\n", entry_pos);
70  if (sep_pos == string::npos) {
71  if (acl_string.length() > entry_pos) {
72  entry_length = acl_string.length() - entry_pos;
73  next_pos = string::npos;
74  } else {
75  // we've just looked past a trailing delimiter
76  break;
77  }
78  } else {
79  assert(sep_pos >= entry_pos);
80  entry_length = sep_pos - entry_pos;
81  next_pos = sep_pos + 1;
82  }
83  if (entry_length == 0) {
84  // libacl tolerates excessive whitespace but not excessive delimiters.
85  // It's simpler for us to treat whitespace as delimiters.
86  entry_pos = next_pos;
87  continue;
88  }
89  string entry(acl_string, entry_pos, entry_length);
90  entry_pos = next_pos;
91 
92  // search for '#'-starting comment, discard if found
93  size_t comment_pos = entry.find('#');
94  if (comment_pos != string::npos) {
95  entry = string(entry, 0, comment_pos);
96  }
97 
98  // TODO(autkin): trim whitespace on both ends
99 
100  // discard empty lines
101  if (entry.length() == 0) {
102  continue;
103  }
104 
105  string_entries.push_back(entry);
106  }
107  return 0;
108 }
109 
110 static int acl_parms_from_text(const string &str, u_int16_t *perms)
111 {
112  // Currently unsupported syntax features found in setfacl:
113  // - X (capital x)
114  // - numeric syntax
115  // See "man 1 setfacl", "The perms field is..."
116 
117  *perms = 0;
118  for (const char &c : str) {
119  switch(c) {
120  case 'r': *perms |= ACL_READ; break;
121  case 'w': *perms |= ACL_WRITE; break;
122  case 'x': *perms |= ACL_EXECUTE; break;
123  case '-': break;
124  default: return EINVAL;
125  }
126  }
127  return 0;
128 }
129 
130 static int acl_entry_from_text(const string &str, acl_ea_entry &entry)
131 {
132  // break down to 3 fields by ':'
133  // type:qualifier:permissions according to terminology
134  // e_tag:e_id:e_perm are acl_ea_entry field names
135  size_t sep_pos = str.find(':');
136  if (sep_pos == string::npos) {
137  return EINVAL;
138  }
139  string const type(str, 0, sep_pos);
140  size_t next_field_pos = sep_pos + 1;
141  sep_pos = str.find(':', next_field_pos);
142  if (sep_pos == string::npos) {
143  return EINVAL;
144  }
145  string const qualifier(str, next_field_pos, sep_pos - next_field_pos);
146  next_field_pos = sep_pos + 1;
147  string permissions(str, next_field_pos);
148 
149  if (!type.compare("user") || !type.compare("u")) {
150  entry.e_tag = qualifier.empty() ? ACL_USER_OBJ : ACL_USER;
151  } else if (!type.compare("group") || !type.compare("g")) {
152  entry.e_tag = qualifier.empty() ? ACL_GROUP_OBJ : ACL_GROUP;
153  } else if (!type.compare("other") || !type.compare("o")) {
154  entry.e_tag = ACL_OTHER;
155  } else if (!type.compare("mask") || !type.compare("m")) {
156  entry.e_tag = ACL_MASK;
157  } else {
158  return EINVAL;
159  }
160  entry.e_tag = htole16(entry.e_tag);
161 
162  if (qualifier.empty()) {
163  entry.e_id = ACL_UNDEFINED_ID;
164  } else {
165  char* at_null_terminator_if_number;
166  long number = strtol(qualifier.c_str(), &at_null_terminator_if_number, 10);
167  if (*at_null_terminator_if_number != '\0') {
168  bool ok;
169  if (entry.e_tag == htole16(ACL_USER)) {
170  [[maybe_unused]] gid_t main_gid;
171  uid_t uid;
172  ok = GetUidOf(qualifier, &uid, &main_gid);
173  number = uid;
174  } else if (entry.e_tag == htole16(ACL_GROUP)) {
175  gid_t gid;
176  ok = GetGidOf(qualifier, &gid);
177  number = gid;
178  } else {
179  assert(false);
180  }
181  if (!ok) {
182  return EINVAL;
183  }
184  }
185  entry.e_id = htole32(number);
186  }
187 
188  // parse perms
189  u_int16_t host_byteorder_perms;
190  int ret;
191  ret = acl_parms_from_text(permissions, &host_byteorder_perms);
192  if (ret) {
193  return ret;
194  }
195  entry.e_perm = htole16(host_byteorder_perms);
196 
197  return 0;
198 }
199 
200 static bool acl_valid_builtin(const vector<acl_ea_entry> &entries)
201 {
202  // From man acl_valid:
203  // The three required entries ACL_USER_OBJ, ACL_GROUP_OBJ, and ACL_OTHER must
204  // exist exactly once in the ACL.
205  //
206  // If the ACL contains any ACL_USER or ACL_GROUP entries, then an ACL_MASK
207  // entry is also required.
208  //
209  // The ACL may contain at most one ACL_MASK entry.
210  //
211  // The user identifiers must be unique among all entries of type ACL_USER.
212  // The group identifiers must be unique among all entries of type ACL_GROUP.
213 
214  bool types_met[ACL_OTHER + 1] = {false, };
215 
216  for (auto entry_it = entries.begin(); entry_it != entries.end(); ++entry_it) {
217  const acl_ea_entry &e = *entry_it;
218  assert(e.e_tag <= ACL_OTHER);
219  bool &type_met = types_met[e.e_tag];
220  switch (e.e_tag) {
221  // at most one of these types
222  case ACL_USER_OBJ:
223  case ACL_GROUP_OBJ:
224  case ACL_OTHER:
225  case ACL_MASK:
226  if (type_met) {
227  return false;
228  } else {
229  type_met = true;
230  }
231  break;
232  case ACL_USER:
233  case ACL_GROUP:
234  type_met = true;
235  break;
236  default:
237  assert(false);
238  }
239  }
240  if (!(types_met[ACL_USER_OBJ] && types_met[ACL_GROUP_OBJ] && types_met[ACL_OTHER])) {
241  return false;
242  }
243  if ((types_met[ACL_USER] || types_met[ACL_GROUP]) && !types_met[ACL_MASK]) {
244  return false;
245  }
246  // TODO(autkin): ACL_USER, ACL_GROUP uniqueness checks. Not a pressing issue.
247  return true;
248 }
249 
250 int acl_from_text_to_xattr_value(const string& textual_acl, char *&o_binary_acl, size_t &o_size, bool &o_equiv_mode)
251 {
252  int ret;
253 
254  o_equiv_mode = true;
255 
256  // get individual textual entries from one big text
257  vector<string> string_entries;
258  ret = acl_from_text_to_string_entries(textual_acl, string_entries);
259  if (ret) {
260  return ret;
261  }
262 
263  // get individual entries in structural form
264  vector<acl_ea_entry> entries;
265  for (auto string_it = string_entries.begin(); string_it != string_entries.end(); ++string_it) {
266  acl_ea_entry entry;
267  ret = acl_entry_from_text(*string_it, entry);
268  if (ret) {
269  return ret;
270  }
271  if (entry.e_tag == ACL_GROUP || entry.e_tag == ACL_USER) {
272  o_equiv_mode = false;
273  }
274  entries.push_back(entry);
275  }
276 
277  // sort entries as libacl does, to be able to use it in testing as a reference
278  sort(entries.begin(), entries.end());
279 
280  // reject what acl_valid() rejects, to be able to use it in testing
281  if (!acl_valid_builtin(entries)) {
282  return EINVAL;
283  }
284 
285  // if nothing but usual u,g,o bits, don't produce a binary. Mimicking libacl.
286  if (o_equiv_mode) {
287  o_binary_acl = NULL;
288  o_size = 0;
289  return 0;
290  }
291 
292  // get one big buffer with all the entries in the "on-disk" xattr format
293  size_t acl_entry_count = entries.size();
294  size_t const buf_size = sizeof(acl_ea_header) + (acl_entry_count * sizeof(acl_ea_entry));
295  char *buf = static_cast<char*>(malloc(buf_size));
296  if (!buf) {
297  return ENOMEM;
298  }
299  acl_ea_header* header = reinterpret_cast<acl_ea_header*>(buf);
300  header->a_version = htole32(ACL_EA_VERSION);
301  acl_ea_entry* ext_entry = reinterpret_cast<acl_ea_entry*>(header + 1);
302  for (auto entry_it = entries.begin(); entry_it != entries.end(); ++entry_it) {
303  *ext_entry = *entry_it;
304  ext_entry += 1;
305  }
306 
307  o_binary_acl = buf;
308  o_size = buf_size;
309  return 0;
310 }
311 
312 #ifdef COMPARE_TO_LIBACL
313 int acl_from_text_to_xattr_value_libacl(const string textual_acl, char *&o_binary_acl, size_t &o_size, bool &o_equiv_mode)
314 {
315  acl_t acl = acl_from_text(textual_acl.c_str()); // Convert ACL string to acl_t object
316  if (!acl) {
317  return EINVAL;
318  }
319  if (acl_valid(acl) != 0) {
320  acl_free(acl);
321  return EINVAL;
322  }
323 
324  // check if the ACL string contains more than the synthetic ACLs
325  int equiv = acl_equiv_mode(acl, NULL);
326  assert(equiv != -1);
327 
328  o_equiv_mode = equiv == 0;
329  if (!o_equiv_mode) {
330  o_binary_acl = (char *)acl_to_xattr(acl, &o_size);
331  } else {
332  o_binary_acl = NULL;
333  o_size = 0;
334  }
335  acl_free(acl);
336  return 0;
337 }
338 
339 int acl_from_text_to_xattr_value_both_impl(const string textual_acl, char *&o_binary_acl, size_t &o_size, bool &o_equiv_mode)
340 {
341  struct impl_result {
342  int ret;
343  size_t binary_size;
344  char *binary_acl;
345  bool equiv_mode;
346  } b, l;
347  b.ret = acl_from_text_to_xattr_value(textual_acl, b.binary_acl, b.binary_size, b.equiv_mode);
348  l.ret = acl_from_text_to_xattr_value_libacl(textual_acl, l.binary_acl, l.binary_size, l.equiv_mode);
349  assert(b.ret == l.ret);
350  if (!l.ret) {
351  assert(b.binary_size == l.binary_size);
352  assert(0 == memcmp(b.binary_acl, l.binary_acl, b.binary_size));
353  assert(b.equiv_mode == l.equiv_mode);
354  free(l.binary_acl);
355  }
356  o_binary_acl = b.binary_acl;
357  o_size = b.binary_size;
358  o_equiv_mode = b.equiv_mode;
359  return b.ret;
360 }
361 #endif // COMPARE_TO_LIBACL
#define ACL_EXECUTE
Definition: acl.cc:25
#define ACL_READ
Definition: acl.cc:23
u_int32_t e_id
Definition: acl.cc:47
#define ACL_MASK
Definition: acl.cc:33
assert((mem||(size==0))&&"Out Of Memory")
#define ACL_USER_OBJ
Definition: acl.cc:29
static bool acl_valid_builtin(const vector< acl_ea_entry > &entries)
Definition: acl.cc:200
u_int16_t e_perm
Definition: acl.cc:46
u_int32_t a_version
Definition: acl.cc:59
#define ACL_USER
Definition: acl.cc:30
static int acl_parms_from_text(const string &str, u_int16_t *perms)
Definition: acl.cc:110
#define ACL_GROUP
Definition: acl.cc:32
#define ACL_EA_VERSION
Definition: acl.cc:41
int acl_from_text_to_xattr_value(const string &textual_acl, char *&o_binary_acl, size_t &o_size, bool &o_equiv_mode)
Definition: acl.cc:250
static int acl_from_text_to_string_entries(const string &acl_string, vector< string > &string_entries)
Definition: acl.cc:63
#define ACL_OTHER
Definition: acl.cc:34
bool GetGidOf(const std::string &groupname, gid_t *gid)
Definition: posix.cc:1365
#define ACL_WRITE
Definition: acl.cc:24
#define ACL_UNDEFINED_ID
Definition: acl.cc:37
#define ACL_GROUP_OBJ
Definition: acl.cc:31
bool GetUidOf(const std::string &username, uid_t *uid, gid_t *main_gid)
Definition: posix.cc:1342
bool operator<(const acl_ea_entry &other) const
Definition: acl.cc:50
u_int16_t e_tag
Definition: acl.cc:45
static int acl_entry_from_text(const string &str, acl_ea_entry &entry)
Definition: acl.cc:130
Definition: acl.cc:44