Line data Source code
1 : /*
2 : Unix SMB/Netbios implementation.
3 : VFS module to get and set posix acls through xattr
4 : Copyright (c) 2013 Anand Avati <avati@redhat.com>
5 : Copyright (c) 2016 Yan, Zheng <zyan@redhat.com>
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "includes.h"
22 : #include "system/filesys.h"
23 : #include "smbd/smbd.h"
24 : #include "modules/posixacl_xattr.h"
25 :
26 : /*
27 : POSIX ACL Format:
28 :
29 : Size = 4 (header) + N * 8 (entry)
30 :
31 : Offset Size Field (Little Endian)
32 : -------------------------------------
33 : 0-3 4-byte Version
34 :
35 : 4-5 2-byte Entry-1 tag
36 : 6-7 2-byte Entry-1 perm
37 : 8-11 4-byte Entry-1 id
38 :
39 : 12-13 2-byte Entry-2 tag
40 : 14-15 2-byte Entry-2 perm
41 : 16-19 4-byte Entry-2 id
42 :
43 : ...
44 :
45 : */
46 :
47 :
48 :
49 : /* private functions */
50 :
51 : #define ACL_EA_ACCESS "system.posix_acl_access"
52 : #define ACL_EA_DEFAULT "system.posix_acl_default"
53 : #define ACL_EA_VERSION 0x0002
54 : #define ACL_EA_HEADER_SIZE 4
55 : #define ACL_EA_ENTRY_SIZE 8
56 :
57 : #define ACL_EA_SIZE(n) (ACL_EA_HEADER_SIZE + ((n) * ACL_EA_ENTRY_SIZE))
58 :
59 0 : static SMB_ACL_T mode_to_smb_acl(mode_t mode, TALLOC_CTX *mem_ctx)
60 : {
61 : struct smb_acl_t *result;
62 : int count;
63 :
64 0 : count = 3;
65 0 : result = sys_acl_init(mem_ctx);
66 0 : if (!result) {
67 0 : return NULL;
68 : }
69 :
70 0 : result->acl = talloc_array(result, struct smb_acl_entry, count);
71 0 : if (!result->acl) {
72 0 : errno = ENOMEM;
73 0 : talloc_free(result);
74 0 : return NULL;
75 : }
76 :
77 0 : result->count = count;
78 :
79 0 : result->acl[0].a_type = SMB_ACL_USER_OBJ;
80 0 : result->acl[0].a_perm = (mode & S_IRWXU) >> 6;
81 :
82 0 : result->acl[1].a_type = SMB_ACL_GROUP_OBJ;
83 0 : result->acl[1].a_perm = (mode & S_IRWXG) >> 3;
84 :
85 0 : result->acl[2].a_type = SMB_ACL_OTHER;
86 0 : result->acl[2].a_perm = mode & S_IRWXO;
87 :
88 0 : return result;
89 : }
90 :
91 0 : static SMB_ACL_T posixacl_xattr_to_smb_acl(const char *buf, size_t xattr_size,
92 : TALLOC_CTX *mem_ctx)
93 : {
94 : int count;
95 : int size;
96 : struct smb_acl_entry *smb_ace;
97 : struct smb_acl_t *result;
98 : int i;
99 : int offset;
100 : uint16_t tag;
101 : uint16_t perm;
102 : uint32_t id;
103 :
104 0 : size = xattr_size;
105 :
106 0 : if (size < ACL_EA_HEADER_SIZE) {
107 : /* ACL should be at least as big as the header (4 bytes) */
108 0 : errno = EINVAL;
109 0 : return NULL;
110 : }
111 :
112 : /* Version is the first 4 bytes of the ACL */
113 0 : if (IVAL(buf, 0) != ACL_EA_VERSION) {
114 0 : DEBUG(0, ("Unknown ACL EA version: %d\n",
115 : IVAL(buf, 0)));
116 0 : errno = EINVAL;
117 0 : return NULL;
118 : }
119 0 : offset = ACL_EA_HEADER_SIZE;
120 :
121 0 : size -= ACL_EA_HEADER_SIZE;
122 0 : if (size % ACL_EA_ENTRY_SIZE) {
123 : /* Size of entries must strictly be a multiple of
124 : size of an ACE (8 bytes)
125 : */
126 0 : DEBUG(0, ("Invalid ACL EA size: %d\n", size));
127 0 : errno = EINVAL;
128 0 : return NULL;
129 : }
130 :
131 0 : count = size / ACL_EA_ENTRY_SIZE;
132 :
133 0 : result = sys_acl_init(mem_ctx);
134 0 : if (!result) {
135 0 : return NULL;
136 : }
137 :
138 0 : result->acl = talloc_array(result, struct smb_acl_entry, count);
139 0 : if (!result->acl) {
140 0 : errno = ENOMEM;
141 0 : talloc_free(result);
142 0 : return NULL;
143 : }
144 :
145 0 : result->count = count;
146 :
147 0 : smb_ace = result->acl;
148 :
149 0 : for (i = 0; i < count; i++) {
150 : /* TAG is the first 2 bytes of an entry */
151 0 : tag = SVAL(buf, offset);
152 0 : offset += 2;
153 :
154 : /* PERM is the next 2 bytes of an entry */
155 0 : perm = SVAL(buf, offset);
156 0 : offset += 2;
157 :
158 : /* ID is the last 4 bytes of an entry */
159 0 : id = IVAL(buf, offset);
160 0 : offset += 4;
161 :
162 0 : switch(tag) {
163 0 : case ACL_USER:
164 0 : smb_ace->a_type = SMB_ACL_USER;
165 0 : break;
166 0 : case ACL_USER_OBJ:
167 0 : smb_ace->a_type = SMB_ACL_USER_OBJ;
168 0 : break;
169 0 : case ACL_GROUP:
170 0 : smb_ace->a_type = SMB_ACL_GROUP;
171 0 : break;
172 0 : case ACL_GROUP_OBJ:
173 0 : smb_ace->a_type = SMB_ACL_GROUP_OBJ;
174 0 : break;
175 0 : case ACL_OTHER:
176 0 : smb_ace->a_type = SMB_ACL_OTHER;
177 0 : break;
178 0 : case ACL_MASK:
179 0 : smb_ace->a_type = SMB_ACL_MASK;
180 0 : break;
181 0 : default:
182 0 : DEBUG(0, ("unknown tag type %d\n", (unsigned int) tag));
183 0 : errno = EINVAL;
184 0 : return NULL;
185 : }
186 :
187 :
188 0 : switch(smb_ace->a_type) {
189 0 : case SMB_ACL_USER:
190 0 : smb_ace->info.user.uid = id;
191 0 : break;
192 0 : case SMB_ACL_GROUP:
193 0 : smb_ace->info.group.gid = id;
194 0 : break;
195 0 : default:
196 0 : break;
197 : }
198 :
199 0 : smb_ace->a_perm = 0;
200 0 : smb_ace->a_perm |= ((perm & ACL_READ) ? SMB_ACL_READ : 0);
201 0 : smb_ace->a_perm |= ((perm & ACL_WRITE) ? SMB_ACL_WRITE : 0);
202 0 : smb_ace->a_perm |= ((perm & ACL_EXECUTE) ? SMB_ACL_EXECUTE : 0);
203 :
204 0 : smb_ace++;
205 : }
206 :
207 0 : return result;
208 : }
209 :
210 :
211 0 : static int posixacl_xattr_entry_compare(const void *left, const void *right)
212 : {
213 0 : int ret = 0;
214 : uint16_t tag_left, tag_right;
215 : uint32_t id_left, id_right;
216 :
217 : /*
218 : Sorting precedence:
219 : - Smaller TAG values must be earlier.
220 : - Within same TAG, smaller identifiers must be earlier, E.g:
221 : UID 0 entry must be earlier than UID 200
222 : GID 17 entry must be earlier than GID 19
223 : */
224 :
225 : /* TAG is the first element in the entry */
226 0 : tag_left = SVAL(left, 0);
227 0 : tag_right = SVAL(right, 0);
228 :
229 0 : ret = (tag_left - tag_right);
230 0 : if (!ret) {
231 : /* ID is the third element in the entry, after two short
232 : integers (tag and perm), i.e at offset 4.
233 : */
234 0 : id_left = IVAL(left, 4);
235 0 : id_right = IVAL(right, 4);
236 0 : ret = id_left - id_right;
237 : }
238 :
239 0 : return ret;
240 : }
241 :
242 :
243 0 : static int smb_acl_to_posixacl_xattr(SMB_ACL_T theacl, char *buf, size_t len)
244 : {
245 : ssize_t size;
246 : struct smb_acl_entry *smb_ace;
247 : int i;
248 : int count;
249 : uint16_t tag;
250 : uint16_t perm;
251 : uint32_t id;
252 : int offset;
253 :
254 0 : count = theacl->count;
255 :
256 0 : size = ACL_EA_SIZE(count);
257 0 : if (!buf) {
258 0 : return size;
259 : }
260 0 : if (len < size) {
261 0 : return -ERANGE;
262 : }
263 0 : smb_ace = theacl->acl;
264 :
265 : /* Version is the first 4 bytes of the ACL */
266 0 : SIVAL(buf, 0, ACL_EA_VERSION);
267 0 : offset = ACL_EA_HEADER_SIZE;
268 :
269 0 : for (i = 0; i < count; i++) {
270 : /* Calculate tag */
271 0 : switch(smb_ace->a_type) {
272 0 : case SMB_ACL_USER:
273 0 : tag = ACL_USER;
274 0 : break;
275 0 : case SMB_ACL_USER_OBJ:
276 0 : tag = ACL_USER_OBJ;
277 0 : break;
278 0 : case SMB_ACL_GROUP:
279 0 : tag = ACL_GROUP;
280 0 : break;
281 0 : case SMB_ACL_GROUP_OBJ:
282 0 : tag = ACL_GROUP_OBJ;
283 0 : break;
284 0 : case SMB_ACL_OTHER:
285 0 : tag = ACL_OTHER;
286 0 : break;
287 0 : case SMB_ACL_MASK:
288 0 : tag = ACL_MASK;
289 0 : break;
290 0 : default:
291 0 : DEBUG(0, ("Unknown tag value %d\n",
292 : smb_ace->a_type));
293 0 : return -EINVAL;
294 : }
295 :
296 :
297 : /* Calculate id */
298 0 : switch(smb_ace->a_type) {
299 0 : case SMB_ACL_USER:
300 0 : id = smb_ace->info.user.uid;
301 0 : break;
302 0 : case SMB_ACL_GROUP:
303 0 : id = smb_ace->info.group.gid;
304 0 : break;
305 0 : default:
306 0 : id = ACL_UNDEFINED_ID;
307 0 : break;
308 : }
309 :
310 : /* Calculate perm */
311 0 : perm = 0;
312 0 : perm |= (smb_ace->a_perm & SMB_ACL_READ) ? ACL_READ : 0;
313 0 : perm |= (smb_ace->a_perm & SMB_ACL_WRITE) ? ACL_WRITE : 0;
314 0 : perm |= (smb_ace->a_perm & SMB_ACL_EXECUTE) ? ACL_EXECUTE : 0;
315 :
316 : /* TAG is the first 2 bytes of an entry */
317 0 : SSVAL(buf, offset, tag);
318 0 : offset += 2;
319 :
320 : /* PERM is the next 2 bytes of an entry */
321 0 : SSVAL(buf, offset, perm);
322 0 : offset += 2;
323 :
324 : /* ID is the last 4 bytes of an entry */
325 0 : SIVAL(buf, offset, id);
326 0 : offset += 4;
327 :
328 0 : smb_ace++;
329 : }
330 :
331 : /* Skip the header, sort @count number of 8-byte entries */
332 0 : qsort(buf+ACL_EA_HEADER_SIZE, count, ACL_EA_ENTRY_SIZE,
333 : posixacl_xattr_entry_compare);
334 :
335 0 : return size;
336 : }
337 :
338 0 : SMB_ACL_T posixacl_xattr_acl_get_fd(vfs_handle_struct *handle,
339 : files_struct *fsp,
340 : SMB_ACL_TYPE_T type,
341 : TALLOC_CTX *mem_ctx)
342 : {
343 : int ret;
344 0 : int size = ACL_EA_SIZE(20);
345 0 : char *buf = alloca(size);
346 : const char *name;
347 :
348 0 : if (type == SMB_ACL_TYPE_ACCESS) {
349 0 : name = ACL_EA_ACCESS;
350 0 : } else if (type == SMB_ACL_TYPE_DEFAULT) {
351 0 : name = ACL_EA_DEFAULT;
352 : } else {
353 0 : errno = EINVAL;
354 0 : return NULL;
355 : }
356 :
357 0 : if (!buf) {
358 0 : return NULL;
359 : }
360 :
361 0 : ret = SMB_VFS_FGETXATTR(fsp, name, buf, size);
362 0 : if (ret < 0 && errno == ERANGE) {
363 0 : size = SMB_VFS_FGETXATTR(fsp, name, NULL, 0);
364 0 : if (size > 0) {
365 0 : buf = alloca(size);
366 0 : if (!buf) {
367 0 : return NULL;
368 : }
369 0 : ret = SMB_VFS_FGETXATTR(fsp, name, buf, size);
370 : }
371 : }
372 :
373 0 : if (ret > 0) {
374 0 : return posixacl_xattr_to_smb_acl(buf, ret, mem_ctx);
375 : }
376 0 : if (ret == 0 || errno == ENOATTR) {
377 : SMB_STRUCT_STAT sbuf;
378 0 : ret = SMB_VFS_FSTAT(fsp, &sbuf);
379 0 : if (ret == 0)
380 0 : return mode_to_smb_acl(sbuf.st_ex_mode, mem_ctx);
381 : }
382 0 : return NULL;
383 : }
384 :
385 0 : int posixacl_xattr_acl_set_fd(vfs_handle_struct *handle,
386 : files_struct *fsp,
387 : SMB_ACL_TYPE_T type,
388 : SMB_ACL_T theacl)
389 : {
390 0 : const char *name = NULL;
391 : char *buf;
392 : ssize_t size;
393 : int ret;
394 :
395 0 : if (type == SMB_ACL_TYPE_ACCESS) {
396 0 : name = ACL_EA_ACCESS;
397 0 : } else if (type == SMB_ACL_TYPE_DEFAULT) {
398 0 : name = ACL_EA_DEFAULT;
399 : } else {
400 0 : errno = EINVAL;
401 0 : return -1;
402 : }
403 :
404 0 : size = smb_acl_to_posixacl_xattr(theacl, NULL, 0);
405 0 : buf = alloca(size);
406 0 : if (!buf) {
407 0 : return -1;
408 : }
409 :
410 0 : ret = smb_acl_to_posixacl_xattr(theacl, buf, size);
411 0 : if (ret < 0) {
412 0 : errno = -ret;
413 0 : return -1;
414 : }
415 :
416 0 : return SMB_VFS_FSETXATTR(fsp, name, buf, size, 0);
417 : }
418 :
419 0 : int posixacl_xattr_acl_delete_def_fd(vfs_handle_struct *handle,
420 : files_struct *fsp)
421 : {
422 0 : return SMB_VFS_FREMOVEXATTR(fsp, ACL_EA_DEFAULT);
423 : }
|