Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Security Descriptor (SD) helper functions
4 :
5 : Copyright (C) Andrew Tridgell 2000
6 : Copyright (C) Tim Potter 2000
7 : Copyright (C) Jeremy Allison 2000
8 : Copyright (C) Jelmer Vernooij 2003
9 :
10 : This program is free software; you can redistribute it and/or modify
11 : it under the terms of the GNU General Public License as published by
12 : the Free Software Foundation; either version 3 of the License, or
13 : (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program. If not, see <http://www.gnu.org/licenses/>.
22 : */
23 :
24 : #include "includes.h"
25 : #include "libsmb/libsmb.h"
26 : #include "util_sd.h"
27 : #include "librpc/gen_ndr/ndr_lsa.h"
28 : #include "../libcli/security/security.h"
29 : #include "rpc_client/cli_pipe.h"
30 : #include "rpc_client/cli_lsarpc.h"
31 : #include "lib/util/string_wrappers.h"
32 :
33 : /* These values discovered by inspection */
34 :
35 : struct perm_value {
36 : const char *perm;
37 : uint32_t mask;
38 : };
39 :
40 : static const struct perm_value special_values[] = {
41 : { "R", SEC_RIGHTS_FILE_READ },
42 : { "W", SEC_RIGHTS_FILE_WRITE },
43 : { "X", SEC_RIGHTS_FILE_EXECUTE },
44 : { "D", SEC_STD_DELETE },
45 : { "P", SEC_STD_WRITE_DAC },
46 : { "O", SEC_STD_WRITE_OWNER },
47 : { NULL, 0 },
48 : };
49 :
50 : static const struct perm_value standard_values[] = {
51 : { "READ", SEC_RIGHTS_DIR_READ|SEC_DIR_TRAVERSE },
52 : { "CHANGE", SEC_RIGHTS_DIR_READ|SEC_STD_DELETE|\
53 : SEC_DIR_DELETE_CHILD|\
54 : SEC_RIGHTS_DIR_WRITE|SEC_DIR_TRAVERSE },
55 : { "FULL", SEC_RIGHTS_DIR_ALL },
56 : { NULL, 0 },
57 : };
58 :
59 : static const struct {
60 : uint16_t mask;
61 : const char *str;
62 : const char *desc;
63 : } sec_desc_ctrl_bits[] = {
64 : {SEC_DESC_OWNER_DEFAULTED, "OD", "Owner Defaulted"},
65 : {SEC_DESC_GROUP_DEFAULTED, "GD", "Group Defaulted"},
66 : {SEC_DESC_DACL_PRESENT, "DP", "DACL Present"},
67 : {SEC_DESC_DACL_DEFAULTED, "DD", "DACL Defaulted"},
68 : {SEC_DESC_SACL_PRESENT, "SP", "SACL Present"},
69 : {SEC_DESC_SACL_DEFAULTED, "SD", "SACL Defaulted"},
70 : {SEC_DESC_DACL_TRUSTED, "DT", "DACL Trusted"},
71 : {SEC_DESC_SERVER_SECURITY, "SS", "Server Security"},
72 : {SEC_DESC_DACL_AUTO_INHERIT_REQ, "DR", "DACL Inheritance Required"},
73 : {SEC_DESC_SACL_AUTO_INHERIT_REQ, "SR", "SACL Inheritance Required"},
74 : {SEC_DESC_DACL_AUTO_INHERITED, "DI", "DACL Auto Inherited"},
75 : {SEC_DESC_SACL_AUTO_INHERITED, "SI", "SACL Auto Inherited"},
76 : {SEC_DESC_DACL_PROTECTED, "PD", "DACL Protected"},
77 : {SEC_DESC_SACL_PROTECTED, "PS", "SACL Protected"},
78 : {SEC_DESC_RM_CONTROL_VALID, "RM", "RM Control Valid"},
79 : {SEC_DESC_SELF_RELATIVE , "SR", "Self Relative"},
80 : };
81 :
82 : /* Open cli connection and policy handle */
83 5742 : static NTSTATUS cli_lsa_lookup_sid(struct cli_state *cli,
84 : const struct dom_sid *sid,
85 : TALLOC_CTX *mem_ctx,
86 : enum lsa_SidType *type,
87 : char **domain, char **name)
88 : {
89 5742 : struct smbXcli_tcon *orig_tcon = NULL;
90 5742 : char *orig_share = NULL;
91 5742 : struct rpc_pipe_client *p = NULL;
92 0 : struct policy_handle handle;
93 0 : NTSTATUS status;
94 5742 : TALLOC_CTX *frame = talloc_stackframe();
95 0 : enum lsa_SidType *types;
96 0 : char **domains;
97 0 : char **names;
98 :
99 5742 : if (cli_state_has_tcon(cli)) {
100 5742 : cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
101 : }
102 :
103 5742 : status = cli_tree_connect(cli, "IPC$", "?????", NULL);
104 5742 : if (!NT_STATUS_IS_OK(status)) {
105 0 : goto tcon_fail;
106 : }
107 :
108 5742 : status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc,
109 : &p);
110 5742 : if (!NT_STATUS_IS_OK(status)) {
111 0 : goto fail;
112 : }
113 :
114 5742 : status = rpccli_lsa_open_policy(p, talloc_tos(), True,
115 : GENERIC_EXECUTE_ACCESS, &handle);
116 5742 : if (!NT_STATUS_IS_OK(status)) {
117 0 : goto fail;
118 : }
119 :
120 5742 : status = rpccli_lsa_lookup_sids(p, talloc_tos(), &handle, 1, sid,
121 : &domains, &names, &types);
122 5742 : if (!NT_STATUS_IS_OK(status)) {
123 0 : goto fail;
124 : }
125 :
126 5742 : *type = types[0];
127 5742 : *domain = talloc_move(mem_ctx, &domains[0]);
128 5742 : *name = talloc_move(mem_ctx, &names[0]);
129 :
130 5742 : status = NT_STATUS_OK;
131 5742 : fail:
132 5742 : TALLOC_FREE(p);
133 5742 : cli_tdis(cli);
134 5742 : tcon_fail:
135 5742 : cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
136 5742 : TALLOC_FREE(frame);
137 5742 : return status;
138 : }
139 :
140 : /* convert a SID to a string, either numeric or username/group */
141 5864 : void SidToString(struct cli_state *cli, fstring str, const struct dom_sid *sid,
142 : bool numeric)
143 : {
144 5864 : char *domain = NULL;
145 5864 : char *name = NULL;
146 0 : enum lsa_SidType type;
147 0 : NTSTATUS status;
148 :
149 5864 : sid_to_fstring(str, sid);
150 :
151 5864 : if (numeric || cli == NULL) {
152 122 : return;
153 : }
154 :
155 5742 : status = cli_lsa_lookup_sid(cli, sid, talloc_tos(), &type,
156 : &domain, &name);
157 :
158 5742 : if (!NT_STATUS_IS_OK(status)) {
159 0 : return;
160 : }
161 :
162 5742 : if (*domain) {
163 2600 : slprintf(str, sizeof(fstring) - 1, "%s%s%s",
164 : domain, lp_winbind_separator(), name);
165 : } else {
166 3142 : fstrcpy(str, name);
167 : }
168 : }
169 :
170 1434 : static NTSTATUS cli_lsa_lookup_name(struct cli_state *cli,
171 : const char *name,
172 : enum lsa_SidType *type,
173 : struct dom_sid *sid)
174 : {
175 1434 : struct smbXcli_tcon *orig_tcon = NULL;
176 1434 : char *orig_share = NULL;
177 1434 : struct rpc_pipe_client *p = NULL;
178 0 : struct policy_handle handle;
179 0 : NTSTATUS status;
180 1434 : TALLOC_CTX *frame = talloc_stackframe();
181 0 : struct dom_sid *sids;
182 0 : enum lsa_SidType *types;
183 :
184 1434 : if (cli_state_has_tcon(cli)) {
185 1434 : cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
186 : }
187 :
188 1434 : status = cli_tree_connect(cli, "IPC$", "?????", NULL);
189 1434 : if (!NT_STATUS_IS_OK(status)) {
190 0 : goto tcon_fail;
191 : }
192 :
193 1434 : status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc,
194 : &p);
195 1434 : if (!NT_STATUS_IS_OK(status)) {
196 0 : goto fail;
197 : }
198 :
199 1434 : status = rpccli_lsa_open_policy(p, talloc_tos(), True,
200 : GENERIC_EXECUTE_ACCESS, &handle);
201 1434 : if (!NT_STATUS_IS_OK(status)) {
202 0 : goto fail;
203 : }
204 :
205 1434 : status = rpccli_lsa_lookup_names(p, talloc_tos(), &handle, 1, &name,
206 : NULL, 1, &sids, &types);
207 1434 : if (!NT_STATUS_IS_OK(status)) {
208 0 : goto fail;
209 : }
210 :
211 1434 : *type = types[0];
212 1434 : *sid = sids[0];
213 :
214 1434 : status = NT_STATUS_OK;
215 1434 : fail:
216 1434 : TALLOC_FREE(p);
217 1434 : cli_tdis(cli);
218 1434 : tcon_fail:
219 1434 : cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
220 1434 : TALLOC_FREE(frame);
221 1434 : return status;
222 : }
223 :
224 : /* convert a string to a SID, either numeric or username/group */
225 1458 : bool StringToSid(struct cli_state *cli, struct dom_sid *sid, const char *str)
226 : {
227 0 : enum lsa_SidType type;
228 :
229 1458 : if (string_to_sid(sid, str)) {
230 24 : return true;
231 : }
232 :
233 1434 : if (cli == NULL) {
234 0 : return false;
235 : }
236 :
237 1434 : return NT_STATUS_IS_OK(cli_lsa_lookup_name(cli, str, &type, sid));
238 : }
239 :
240 4588 : static void print_ace_flags(FILE *f, uint8_t flags)
241 : {
242 4588 : char *str = talloc_strdup(NULL, "");
243 0 : size_t len;
244 :
245 4588 : if (flags & SEC_ACE_FLAG_OBJECT_INHERIT) {
246 1824 : talloc_asprintf_addbuf(&str, "OI|");
247 : }
248 4588 : if (flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
249 1848 : talloc_asprintf_addbuf(&str, "CI|");
250 : }
251 4588 : if (flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
252 18 : talloc_asprintf_addbuf(&str, "NP|");
253 : }
254 4588 : if (flags & SEC_ACE_FLAG_INHERIT_ONLY) {
255 1044 : talloc_asprintf_addbuf(&str, "IO|");
256 : }
257 4588 : if (flags & SEC_ACE_FLAG_INHERITED_ACE) {
258 2562 : talloc_asprintf_addbuf(&str, "I|");
259 : }
260 4588 : if (str == NULL) {
261 0 : goto out;
262 : }
263 :
264 : /* Ignore define SEC_ACE_FLAG_SUCCESSFUL_ACCESS ( 0x40 )
265 : and SEC_ACE_FLAG_FAILED_ACCESS ( 0x80 ) as they're
266 : audit ace flags. */
267 :
268 4588 : len = strlen(str);
269 4588 : if (len > 0) {
270 3498 : fprintf(f, "/%.*s/", (int)len-1, str);
271 : } else {
272 1090 : fprintf(f, "/0x%x/", flags);
273 : }
274 4588 : TALLOC_FREE(str);
275 4588 : return;
276 :
277 0 : out:
278 0 : fprintf(f, "/0x%x/", flags);
279 : }
280 :
281 : /* print an ACE on a FILE, using either numeric or ascii representation */
282 4588 : void print_ace(struct cli_state *cli, FILE *f, struct security_ace *ace,
283 : bool numeric)
284 : {
285 0 : const struct perm_value *v;
286 0 : fstring sidstr;
287 4588 : int do_print = 0;
288 0 : uint32_t got_mask;
289 :
290 4588 : SidToString(cli, sidstr, &ace->trustee, numeric);
291 :
292 4588 : fprintf(f, "%s:", sidstr);
293 :
294 4588 : if (numeric) {
295 0 : fprintf(f, "%d/0x%x/0x%08x",
296 0 : ace->type, ace->flags, ace->access_mask);
297 4480 : return;
298 : }
299 :
300 : /* Ace type */
301 :
302 4588 : if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
303 4556 : fprintf(f, "ALLOWED");
304 32 : } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
305 32 : fprintf(f, "DENIED");
306 : } else {
307 0 : fprintf(f, "%d", ace->type);
308 : }
309 :
310 4588 : print_ace_flags(f, ace->flags);
311 :
312 : /* Standard permissions */
313 :
314 8042 : for (v = standard_values; v->perm; v++) {
315 7934 : if (ace->access_mask == v->mask) {
316 4480 : fprintf(f, "%s", v->perm);
317 4480 : return;
318 : }
319 : }
320 :
321 : /* Special permissions. Print out a hex value if we have
322 : leftover bits in the mask. */
323 :
324 108 : got_mask = ace->access_mask;
325 :
326 212 : again:
327 1484 : for (v = special_values; v->perm; v++) {
328 1272 : if ((ace->access_mask & v->mask) == v->mask) {
329 652 : if (do_print) {
330 324 : fprintf(f, "%s", v->perm);
331 : }
332 652 : got_mask &= ~v->mask;
333 : }
334 : }
335 :
336 212 : if (!do_print) {
337 108 : if (got_mask != 0) {
338 4 : fprintf(f, "0x%08x", ace->access_mask);
339 : } else {
340 104 : do_print = 1;
341 104 : goto again;
342 : }
343 : }
344 : }
345 :
346 1230 : static bool parse_ace_flags(const char *str, unsigned int *pflags)
347 : {
348 1230 : const char *p = str;
349 1230 : *pflags = 0;
350 :
351 3582 : while (*p) {
352 2352 : if (strnequal(p, "OI", 2)) {
353 666 : *pflags |= SEC_ACE_FLAG_OBJECT_INHERIT;
354 666 : p += 2;
355 1686 : } else if (strnequal(p, "CI", 2)) {
356 690 : *pflags |= SEC_ACE_FLAG_CONTAINER_INHERIT;
357 690 : p += 2;
358 996 : } else if (strnequal(p, "NP", 2)) {
359 54 : *pflags |= SEC_ACE_FLAG_NO_PROPAGATE_INHERIT;
360 54 : p += 2;
361 942 : } else if (strnequal(p, "IO", 2)) {
362 12 : *pflags |= SEC_ACE_FLAG_INHERIT_ONLY;
363 12 : p += 2;
364 930 : } else if (*p == 'I') {
365 930 : *pflags |= SEC_ACE_FLAG_INHERITED_ACE;
366 930 : p += 1;
367 0 : } else if (*p) {
368 0 : return false;
369 : }
370 :
371 2352 : switch (*p) {
372 1122 : case '|':
373 1122 : p++;
374 :
375 0 : FALL_THROUGH;
376 2352 : case '\0':
377 2352 : continue;
378 0 : default:
379 0 : return false;
380 : }
381 : }
382 1230 : return true;
383 : }
384 :
385 : /* parse an ACE in the same format as print_ace() */
386 1394 : bool parse_ace(struct cli_state *cli, struct security_ace *ace,
387 : const char *orig_str)
388 : {
389 0 : char *p;
390 0 : const char *cp;
391 0 : char *tok;
392 1394 : unsigned int atype = 0;
393 1394 : unsigned int aflags = 0;
394 1394 : unsigned int amask = 0;
395 0 : struct dom_sid sid;
396 0 : uint32_t mask;
397 0 : const struct perm_value *v;
398 1394 : char *str = SMB_STRDUP(orig_str);
399 1394 : TALLOC_CTX *frame = talloc_stackframe();
400 :
401 1394 : if (!str) {
402 0 : TALLOC_FREE(frame);
403 0 : return False;
404 : }
405 :
406 1394 : ZERO_STRUCTP(ace);
407 1394 : p = strchr_m(str,':');
408 1394 : if (!p) {
409 0 : printf("ACE '%s': missing ':'.\n", orig_str);
410 0 : SAFE_FREE(str);
411 0 : TALLOC_FREE(frame);
412 0 : return False;
413 : }
414 1394 : *p = '\0';
415 1394 : p++;
416 :
417 1394 : if (!StringToSid(cli, &sid, str)) {
418 0 : printf("ACE '%s': failed to convert '%s' to SID\n",
419 : orig_str, str);
420 0 : SAFE_FREE(str);
421 0 : TALLOC_FREE(frame);
422 0 : return False;
423 : }
424 :
425 1394 : cp = p;
426 1394 : if (!next_token_talloc(frame, &cp, &tok, "/")) {
427 0 : printf("ACE '%s': failed to find '/' character.\n",
428 : orig_str);
429 0 : SAFE_FREE(str);
430 0 : TALLOC_FREE(frame);
431 0 : return False;
432 : }
433 :
434 1394 : if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
435 1380 : atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
436 14 : } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
437 10 : atype = SEC_ACE_TYPE_ACCESS_DENIED;
438 :
439 4 : } else if (strnequal(tok, "0x", 2)) {
440 0 : int result;
441 :
442 2 : result = sscanf(tok, "%x", &atype);
443 2 : if (result == 0 ||
444 2 : (atype != SEC_ACE_TYPE_ACCESS_ALLOWED &&
445 2 : atype != SEC_ACE_TYPE_ACCESS_DENIED)) {
446 0 : printf("ACE '%s': bad hex value for type at '%s'\n",
447 : orig_str, tok);
448 0 : SAFE_FREE(str);
449 0 : TALLOC_FREE(frame);
450 0 : return false;
451 : }
452 4 : } else if(tok[0] >= '0' && tok[0] <= '9') {
453 0 : int result;
454 :
455 2 : result = sscanf(tok, "%u", &atype);
456 2 : if (result == 0 ||
457 2 : (atype != SEC_ACE_TYPE_ACCESS_ALLOWED &&
458 2 : atype != SEC_ACE_TYPE_ACCESS_DENIED)) {
459 0 : printf("ACE '%s': bad integer value for type at '%s'\n",
460 : orig_str, tok);
461 0 : SAFE_FREE(str);
462 0 : TALLOC_FREE(frame);
463 0 : return false;
464 : }
465 : } else {
466 0 : printf("ACE '%s': missing 'ALLOWED' or 'DENIED' entry at '%s'\n",
467 : orig_str, tok);
468 0 : SAFE_FREE(str);
469 0 : TALLOC_FREE(frame);
470 0 : return False;
471 : }
472 :
473 1394 : if (!next_token_talloc(frame, &cp, &tok, "/")) {
474 0 : printf("ACE '%s': bad flags entry at '%s'\n",
475 : orig_str, tok);
476 0 : SAFE_FREE(str);
477 0 : TALLOC_FREE(frame);
478 0 : return False;
479 : }
480 :
481 1394 : if (tok[0] < '0' || tok[0] > '9') {
482 1230 : if (!parse_ace_flags(tok, &aflags)) {
483 0 : printf("ACE '%s': bad named flags entry at '%s'\n",
484 : orig_str, tok);
485 0 : SAFE_FREE(str);
486 0 : TALLOC_FREE(frame);
487 0 : return False;
488 : }
489 164 : } else if (strnequal(tok, "0x", 2)) {
490 162 : if (!sscanf(tok, "%x", &aflags)) {
491 0 : printf("ACE '%s': bad hex flags entry at '%s'\n",
492 : orig_str, tok);
493 0 : SAFE_FREE(str);
494 0 : TALLOC_FREE(frame);
495 0 : return False;
496 : }
497 : } else {
498 2 : if (!sscanf(tok, "%u", &aflags)) {
499 0 : printf("ACE '%s': bad integer flags entry at '%s'\n",
500 : orig_str, tok);
501 0 : SAFE_FREE(str);
502 0 : TALLOC_FREE(frame);
503 0 : return False;
504 : }
505 : }
506 :
507 1394 : if (!next_token_talloc(frame, &cp, &tok, "/")) {
508 0 : printf("ACE '%s': missing / at '%s'\n",
509 : orig_str, tok);
510 0 : SAFE_FREE(str);
511 0 : TALLOC_FREE(frame);
512 0 : return False;
513 : }
514 :
515 1394 : if (strncmp(tok, "0x", 2) == 0) {
516 12 : if (sscanf(tok, "%x", &amask) != 1) {
517 0 : printf("ACE '%s': bad hex number at '%s'\n",
518 : orig_str, tok);
519 0 : SAFE_FREE(str);
520 0 : TALLOC_FREE(frame);
521 0 : return False;
522 : }
523 12 : goto done;
524 : }
525 :
526 3814 : for (v = standard_values; v->perm; v++) {
527 3684 : if (strcmp(tok, v->perm) == 0) {
528 1252 : amask = v->mask;
529 1252 : goto done;
530 : }
531 : }
532 :
533 130 : p = tok;
534 :
535 444 : while(*p) {
536 314 : bool found = False;
537 :
538 2198 : for (v = special_values; v->perm; v++) {
539 1884 : if (v->perm[0] == *p) {
540 314 : amask |= v->mask;
541 314 : found = True;
542 : }
543 : }
544 :
545 314 : if (!found) {
546 0 : printf("ACE '%s': bad permission value at '%s'\n",
547 : orig_str, p);
548 0 : SAFE_FREE(str);
549 0 : TALLOC_FREE(frame);
550 0 : return False;
551 : }
552 314 : p++;
553 : }
554 :
555 130 : if (*p) {
556 0 : TALLOC_FREE(frame);
557 0 : SAFE_FREE(str);
558 0 : return False;
559 : }
560 :
561 130 : done:
562 1394 : mask = amask;
563 1394 : init_sec_ace(ace, &sid, atype, mask, aflags);
564 1394 : TALLOC_FREE(frame);
565 1394 : SAFE_FREE(str);
566 1394 : return True;
567 : }
568 :
569 692 : static void print_acl_ctrl(FILE *file, uint16_t ctrl, bool numeric)
570 : {
571 0 : int i;
572 692 : const char* separator = "";
573 :
574 692 : fprintf(file, "CONTROL:");
575 692 : if (numeric) {
576 0 : fprintf(file, "0x%x\n", ctrl);
577 0 : return;
578 : }
579 :
580 11764 : for (i = ARRAY_SIZE(sec_desc_ctrl_bits) - 1; i >= 0; i--) {
581 11072 : if (ctrl & sec_desc_ctrl_bits[i].mask) {
582 1390 : fprintf(file, "%s%s",
583 1390 : separator, sec_desc_ctrl_bits[i].str);
584 1390 : separator = "|";
585 : }
586 : }
587 692 : fputc('\n', file);
588 : }
589 :
590 : /* print a ascii version of a security descriptor on a FILE handle */
591 692 : void sec_desc_print(struct cli_state *cli, FILE *f,
592 : struct security_descriptor *sd, bool numeric)
593 : {
594 0 : fstring sidstr;
595 0 : uint32_t i;
596 :
597 692 : fprintf(f, "REVISION:%d\n", sd->revision);
598 692 : print_acl_ctrl(f, sd->type, numeric);
599 :
600 : /* Print owner and group sid */
601 :
602 692 : if (sd->owner_sid) {
603 638 : SidToString(cli, sidstr, sd->owner_sid, numeric);
604 : } else {
605 54 : fstrcpy(sidstr, "");
606 : }
607 :
608 692 : fprintf(f, "OWNER:%s\n", sidstr);
609 :
610 692 : if (sd->group_sid) {
611 638 : SidToString(cli, sidstr, sd->group_sid, numeric);
612 : } else {
613 54 : fstrcpy(sidstr, "");
614 : }
615 :
616 692 : fprintf(f, "GROUP:%s\n", sidstr);
617 :
618 : /* Print aces */
619 5254 : for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
620 4562 : struct security_ace *ace = &sd->dacl->aces[i];
621 4562 : fprintf(f, "ACL:");
622 4562 : print_ace(cli, f, ace, numeric);
623 4562 : fprintf(f, "\n");
624 : }
625 :
626 692 : }
|