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 0 : 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 0 : struct smbXcli_tcon *orig_tcon = NULL;
90 0 : struct rpc_pipe_client *p = NULL;
91 : struct policy_handle handle;
92 : NTSTATUS status;
93 0 : TALLOC_CTX *frame = talloc_stackframe();
94 : enum lsa_SidType *types;
95 : char **domains;
96 : char **names;
97 :
98 0 : if (cli_state_has_tcon(cli)) {
99 0 : orig_tcon = cli_state_save_tcon(cli);
100 0 : if (orig_tcon == NULL) {
101 0 : status = NT_STATUS_NO_MEMORY;
102 0 : goto tcon_fail;
103 : }
104 : }
105 :
106 0 : status = cli_tree_connect(cli, "IPC$", "?????", NULL);
107 0 : if (!NT_STATUS_IS_OK(status)) {
108 0 : goto tcon_fail;
109 : }
110 :
111 0 : status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc,
112 : &p);
113 0 : if (!NT_STATUS_IS_OK(status)) {
114 0 : goto fail;
115 : }
116 :
117 0 : status = rpccli_lsa_open_policy(p, talloc_tos(), True,
118 : GENERIC_EXECUTE_ACCESS, &handle);
119 0 : if (!NT_STATUS_IS_OK(status)) {
120 0 : goto fail;
121 : }
122 :
123 0 : status = rpccli_lsa_lookup_sids(p, talloc_tos(), &handle, 1, sid,
124 : &domains, &names, &types);
125 0 : if (!NT_STATUS_IS_OK(status)) {
126 0 : goto fail;
127 : }
128 :
129 0 : *type = types[0];
130 0 : *domain = talloc_move(mem_ctx, &domains[0]);
131 0 : *name = talloc_move(mem_ctx, &names[0]);
132 :
133 0 : status = NT_STATUS_OK;
134 0 : fail:
135 0 : TALLOC_FREE(p);
136 0 : cli_tdis(cli);
137 0 : tcon_fail:
138 0 : cli_state_restore_tcon(cli, orig_tcon);
139 0 : TALLOC_FREE(frame);
140 0 : return status;
141 : }
142 :
143 : /* convert a SID to a string, either numeric or username/group */
144 0 : void SidToString(struct cli_state *cli, fstring str, const struct dom_sid *sid,
145 : bool numeric)
146 : {
147 0 : char *domain = NULL;
148 0 : char *name = NULL;
149 : enum lsa_SidType type;
150 : NTSTATUS status;
151 :
152 0 : sid_to_fstring(str, sid);
153 :
154 0 : if (numeric || cli == NULL) {
155 0 : return;
156 : }
157 :
158 0 : status = cli_lsa_lookup_sid(cli, sid, talloc_tos(), &type,
159 : &domain, &name);
160 :
161 0 : if (!NT_STATUS_IS_OK(status)) {
162 0 : return;
163 : }
164 :
165 0 : if (*domain) {
166 0 : slprintf(str, sizeof(fstring) - 1, "%s%s%s",
167 : domain, lp_winbind_separator(), name);
168 : } else {
169 0 : fstrcpy(str, name);
170 : }
171 : }
172 :
173 0 : static NTSTATUS cli_lsa_lookup_name(struct cli_state *cli,
174 : const char *name,
175 : enum lsa_SidType *type,
176 : struct dom_sid *sid)
177 : {
178 0 : struct smbXcli_tcon *orig_tcon = NULL;
179 0 : struct rpc_pipe_client *p = NULL;
180 : struct policy_handle handle;
181 : NTSTATUS status;
182 0 : TALLOC_CTX *frame = talloc_stackframe();
183 : struct dom_sid *sids;
184 : enum lsa_SidType *types;
185 :
186 0 : if (cli_state_has_tcon(cli)) {
187 0 : orig_tcon = cli_state_save_tcon(cli);
188 0 : if (orig_tcon == NULL) {
189 0 : status = NT_STATUS_NO_MEMORY;
190 0 : goto tcon_fail;
191 : }
192 : }
193 :
194 0 : status = cli_tree_connect(cli, "IPC$", "?????", NULL);
195 0 : if (!NT_STATUS_IS_OK(status)) {
196 0 : goto tcon_fail;
197 : }
198 :
199 0 : status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc,
200 : &p);
201 0 : if (!NT_STATUS_IS_OK(status)) {
202 0 : goto fail;
203 : }
204 :
205 0 : status = rpccli_lsa_open_policy(p, talloc_tos(), True,
206 : GENERIC_EXECUTE_ACCESS, &handle);
207 0 : if (!NT_STATUS_IS_OK(status)) {
208 0 : goto fail;
209 : }
210 :
211 0 : status = rpccli_lsa_lookup_names(p, talloc_tos(), &handle, 1, &name,
212 : NULL, 1, &sids, &types);
213 0 : if (!NT_STATUS_IS_OK(status)) {
214 0 : goto fail;
215 : }
216 :
217 0 : *type = types[0];
218 0 : *sid = sids[0];
219 :
220 0 : status = NT_STATUS_OK;
221 0 : fail:
222 0 : TALLOC_FREE(p);
223 0 : cli_tdis(cli);
224 0 : tcon_fail:
225 0 : cli_state_restore_tcon(cli, orig_tcon);
226 0 : TALLOC_FREE(frame);
227 0 : return status;
228 : }
229 :
230 : /* convert a string to a SID, either numeric or username/group */
231 0 : bool StringToSid(struct cli_state *cli, struct dom_sid *sid, const char *str)
232 : {
233 : enum lsa_SidType type;
234 :
235 0 : if (string_to_sid(sid, str)) {
236 0 : return true;
237 : }
238 :
239 0 : if (cli == NULL) {
240 0 : return false;
241 : }
242 :
243 0 : return NT_STATUS_IS_OK(cli_lsa_lookup_name(cli, str, &type, sid));
244 : }
245 :
246 0 : static void print_ace_flags(FILE *f, uint8_t flags)
247 : {
248 0 : char *str = talloc_strdup(NULL, "");
249 :
250 0 : if (!str) {
251 0 : goto out;
252 : }
253 :
254 0 : if (flags & SEC_ACE_FLAG_OBJECT_INHERIT) {
255 0 : str = talloc_asprintf(str, "%s%s",
256 : str, "OI|");
257 0 : if (!str) {
258 0 : goto out;
259 : }
260 : }
261 0 : if (flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
262 0 : str = talloc_asprintf(str, "%s%s",
263 : str, "CI|");
264 0 : if (!str) {
265 0 : goto out;
266 : }
267 : }
268 0 : if (flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
269 0 : str = talloc_asprintf(str, "%s%s",
270 : str, "NP|");
271 0 : if (!str) {
272 0 : goto out;
273 : }
274 : }
275 0 : if (flags & SEC_ACE_FLAG_INHERIT_ONLY) {
276 0 : str = talloc_asprintf(str, "%s%s",
277 : str, "IO|");
278 0 : if (!str) {
279 0 : goto out;
280 : }
281 : }
282 0 : if (flags & SEC_ACE_FLAG_INHERITED_ACE) {
283 0 : str = talloc_asprintf(str, "%s%s",
284 : str, "I|");
285 0 : if (!str) {
286 0 : goto out;
287 : }
288 : }
289 : /* Ignore define SEC_ACE_FLAG_SUCCESSFUL_ACCESS ( 0x40 )
290 : and SEC_ACE_FLAG_FAILED_ACCESS ( 0x80 ) as they're
291 : audit ace flags. */
292 :
293 0 : if (str[strlen(str)-1] == '|') {
294 0 : str[strlen(str)-1] = '\0';
295 0 : fprintf(f, "/%s/", str);
296 : } else {
297 0 : fprintf(f, "/0x%x/", flags);
298 : }
299 0 : TALLOC_FREE(str);
300 0 : return;
301 :
302 0 : out:
303 0 : fprintf(f, "/0x%x/", flags);
304 : }
305 :
306 : /* print an ACE on a FILE, using either numeric or ascii representation */
307 0 : void print_ace(struct cli_state *cli, FILE *f, struct security_ace *ace,
308 : bool numeric)
309 : {
310 : const struct perm_value *v;
311 : fstring sidstr;
312 0 : int do_print = 0;
313 : uint32_t got_mask;
314 :
315 0 : SidToString(cli, sidstr, &ace->trustee, numeric);
316 :
317 0 : fprintf(f, "%s:", sidstr);
318 :
319 0 : if (numeric) {
320 0 : fprintf(f, "%d/0x%x/0x%08x",
321 0 : ace->type, ace->flags, ace->access_mask);
322 0 : return;
323 : }
324 :
325 : /* Ace type */
326 :
327 0 : if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
328 0 : fprintf(f, "ALLOWED");
329 0 : } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
330 0 : fprintf(f, "DENIED");
331 : } else {
332 0 : fprintf(f, "%d", ace->type);
333 : }
334 :
335 0 : print_ace_flags(f, ace->flags);
336 :
337 : /* Standard permissions */
338 :
339 0 : for (v = standard_values; v->perm; v++) {
340 0 : if (ace->access_mask == v->mask) {
341 0 : fprintf(f, "%s", v->perm);
342 0 : return;
343 : }
344 : }
345 :
346 : /* Special permissions. Print out a hex value if we have
347 : leftover bits in the mask. */
348 :
349 0 : got_mask = ace->access_mask;
350 :
351 0 : again:
352 0 : for (v = special_values; v->perm; v++) {
353 0 : if ((ace->access_mask & v->mask) == v->mask) {
354 0 : if (do_print) {
355 0 : fprintf(f, "%s", v->perm);
356 : }
357 0 : got_mask &= ~v->mask;
358 : }
359 : }
360 :
361 0 : if (!do_print) {
362 0 : if (got_mask != 0) {
363 0 : fprintf(f, "0x%08x", ace->access_mask);
364 : } else {
365 0 : do_print = 1;
366 0 : goto again;
367 : }
368 : }
369 : }
370 :
371 0 : static bool parse_ace_flags(const char *str, unsigned int *pflags)
372 : {
373 0 : const char *p = str;
374 0 : *pflags = 0;
375 :
376 0 : while (*p) {
377 0 : if (strnequal(p, "OI", 2)) {
378 0 : *pflags |= SEC_ACE_FLAG_OBJECT_INHERIT;
379 0 : p += 2;
380 0 : } else if (strnequal(p, "CI", 2)) {
381 0 : *pflags |= SEC_ACE_FLAG_CONTAINER_INHERIT;
382 0 : p += 2;
383 0 : } else if (strnequal(p, "NP", 2)) {
384 0 : *pflags |= SEC_ACE_FLAG_NO_PROPAGATE_INHERIT;
385 0 : p += 2;
386 0 : } else if (strnequal(p, "IO", 2)) {
387 0 : *pflags |= SEC_ACE_FLAG_INHERIT_ONLY;
388 0 : p += 2;
389 0 : } else if (*p == 'I') {
390 0 : *pflags |= SEC_ACE_FLAG_INHERITED_ACE;
391 0 : p += 1;
392 0 : } else if (*p) {
393 0 : return false;
394 : }
395 :
396 0 : switch (*p) {
397 0 : case '|':
398 0 : p++;
399 :
400 : FALL_THROUGH;
401 0 : case '\0':
402 0 : continue;
403 0 : default:
404 0 : return false;
405 : }
406 : }
407 0 : return true;
408 : }
409 :
410 : /* parse an ACE in the same format as print_ace() */
411 0 : bool parse_ace(struct cli_state *cli, struct security_ace *ace,
412 : const char *orig_str)
413 : {
414 : char *p;
415 : const char *cp;
416 : char *tok;
417 0 : unsigned int atype = 0;
418 0 : unsigned int aflags = 0;
419 0 : unsigned int amask = 0;
420 : struct dom_sid sid;
421 : uint32_t mask;
422 : const struct perm_value *v;
423 0 : char *str = SMB_STRDUP(orig_str);
424 0 : TALLOC_CTX *frame = talloc_stackframe();
425 :
426 0 : if (!str) {
427 0 : TALLOC_FREE(frame);
428 0 : return False;
429 : }
430 :
431 0 : ZERO_STRUCTP(ace);
432 0 : p = strchr_m(str,':');
433 0 : if (!p) {
434 0 : printf("ACE '%s': missing ':'.\n", orig_str);
435 0 : SAFE_FREE(str);
436 0 : TALLOC_FREE(frame);
437 0 : return False;
438 : }
439 0 : *p = '\0';
440 0 : p++;
441 :
442 0 : if (!StringToSid(cli, &sid, str)) {
443 0 : printf("ACE '%s': failed to convert '%s' to SID\n",
444 : orig_str, str);
445 0 : SAFE_FREE(str);
446 0 : TALLOC_FREE(frame);
447 0 : return False;
448 : }
449 :
450 0 : cp = p;
451 0 : if (!next_token_talloc(frame, &cp, &tok, "/")) {
452 0 : printf("ACE '%s': failed to find '/' character.\n",
453 : orig_str);
454 0 : SAFE_FREE(str);
455 0 : TALLOC_FREE(frame);
456 0 : return False;
457 : }
458 :
459 0 : if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
460 0 : atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
461 0 : } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
462 0 : atype = SEC_ACE_TYPE_ACCESS_DENIED;
463 :
464 0 : } else if (strnequal(tok, "0x", 2)) {
465 : int result;
466 :
467 0 : result = sscanf(tok, "%x", &atype);
468 0 : if (result == 0 ||
469 0 : (atype != SEC_ACE_TYPE_ACCESS_ALLOWED &&
470 0 : atype != SEC_ACE_TYPE_ACCESS_DENIED)) {
471 0 : printf("ACE '%s': bad hex value for type at '%s'\n",
472 : orig_str, tok);
473 0 : SAFE_FREE(str);
474 0 : TALLOC_FREE(frame);
475 0 : return false;
476 : }
477 0 : } else if(tok[0] >= '0' && tok[0] <= '9') {
478 : int result;
479 :
480 0 : result = sscanf(tok, "%u", &atype);
481 0 : if (result == 0 ||
482 0 : (atype != SEC_ACE_TYPE_ACCESS_ALLOWED &&
483 0 : atype != SEC_ACE_TYPE_ACCESS_DENIED)) {
484 0 : printf("ACE '%s': bad integer value for type at '%s'\n",
485 : orig_str, tok);
486 0 : SAFE_FREE(str);
487 0 : TALLOC_FREE(frame);
488 0 : return false;
489 : }
490 : } else {
491 0 : printf("ACE '%s': missing 'ALLOWED' or 'DENIED' entry at '%s'\n",
492 : orig_str, tok);
493 0 : SAFE_FREE(str);
494 0 : TALLOC_FREE(frame);
495 0 : return False;
496 : }
497 :
498 0 : if (!next_token_talloc(frame, &cp, &tok, "/")) {
499 0 : printf("ACE '%s': bad 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 0 : if (tok[0] < '0' || tok[0] > '9') {
507 0 : if (!parse_ace_flags(tok, &aflags)) {
508 0 : printf("ACE '%s': bad named flags entry at '%s'\n",
509 : orig_str, tok);
510 0 : SAFE_FREE(str);
511 0 : TALLOC_FREE(frame);
512 0 : return False;
513 : }
514 0 : } else if (strnequal(tok, "0x", 2)) {
515 0 : if (!sscanf(tok, "%x", &aflags)) {
516 0 : printf("ACE '%s': bad hex flags entry at '%s'\n",
517 : orig_str, tok);
518 0 : SAFE_FREE(str);
519 0 : TALLOC_FREE(frame);
520 0 : return False;
521 : }
522 : } else {
523 0 : if (!sscanf(tok, "%u", &aflags)) {
524 0 : printf("ACE '%s': bad integer flags entry at '%s'\n",
525 : orig_str, tok);
526 0 : SAFE_FREE(str);
527 0 : TALLOC_FREE(frame);
528 0 : return False;
529 : }
530 : }
531 :
532 0 : if (!next_token_talloc(frame, &cp, &tok, "/")) {
533 0 : printf("ACE '%s': missing / at '%s'\n",
534 : orig_str, tok);
535 0 : SAFE_FREE(str);
536 0 : TALLOC_FREE(frame);
537 0 : return False;
538 : }
539 :
540 0 : if (strncmp(tok, "0x", 2) == 0) {
541 0 : if (sscanf(tok, "%x", &amask) != 1) {
542 0 : printf("ACE '%s': bad hex number at '%s'\n",
543 : orig_str, tok);
544 0 : SAFE_FREE(str);
545 0 : TALLOC_FREE(frame);
546 0 : return False;
547 : }
548 0 : goto done;
549 : }
550 :
551 0 : for (v = standard_values; v->perm; v++) {
552 0 : if (strcmp(tok, v->perm) == 0) {
553 0 : amask = v->mask;
554 0 : goto done;
555 : }
556 : }
557 :
558 0 : p = tok;
559 :
560 0 : while(*p) {
561 0 : bool found = False;
562 :
563 0 : for (v = special_values; v->perm; v++) {
564 0 : if (v->perm[0] == *p) {
565 0 : amask |= v->mask;
566 0 : found = True;
567 : }
568 : }
569 :
570 0 : if (!found) {
571 0 : printf("ACE '%s': bad permission value at '%s'\n",
572 : orig_str, p);
573 0 : SAFE_FREE(str);
574 0 : TALLOC_FREE(frame);
575 0 : return False;
576 : }
577 0 : p++;
578 : }
579 :
580 0 : if (*p) {
581 0 : TALLOC_FREE(frame);
582 0 : SAFE_FREE(str);
583 0 : return False;
584 : }
585 :
586 0 : done:
587 0 : mask = amask;
588 0 : init_sec_ace(ace, &sid, atype, mask, aflags);
589 0 : TALLOC_FREE(frame);
590 0 : SAFE_FREE(str);
591 0 : return True;
592 : }
593 :
594 0 : static void print_acl_ctrl(FILE *file, uint16_t ctrl, bool numeric)
595 : {
596 : int i;
597 0 : const char* separator = "";
598 :
599 0 : fprintf(file, "CONTROL:");
600 0 : if (numeric) {
601 0 : fprintf(file, "0x%x\n", ctrl);
602 0 : return;
603 : }
604 :
605 0 : for (i = ARRAY_SIZE(sec_desc_ctrl_bits) - 1; i >= 0; i--) {
606 0 : if (ctrl & sec_desc_ctrl_bits[i].mask) {
607 0 : fprintf(file, "%s%s",
608 0 : separator, sec_desc_ctrl_bits[i].str);
609 0 : separator = "|";
610 : }
611 : }
612 0 : fputc('\n', file);
613 : }
614 :
615 : /* print a ascii version of a security descriptor on a FILE handle */
616 0 : void sec_desc_print(struct cli_state *cli, FILE *f,
617 : struct security_descriptor *sd, bool numeric)
618 : {
619 : fstring sidstr;
620 : uint32_t i;
621 :
622 0 : fprintf(f, "REVISION:%d\n", sd->revision);
623 0 : print_acl_ctrl(f, sd->type, numeric);
624 :
625 : /* Print owner and group sid */
626 :
627 0 : if (sd->owner_sid) {
628 0 : SidToString(cli, sidstr, sd->owner_sid, numeric);
629 : } else {
630 0 : fstrcpy(sidstr, "");
631 : }
632 :
633 0 : fprintf(f, "OWNER:%s\n", sidstr);
634 :
635 0 : if (sd->group_sid) {
636 0 : SidToString(cli, sidstr, sd->group_sid, numeric);
637 : } else {
638 0 : fstrcpy(sidstr, "");
639 : }
640 :
641 0 : fprintf(f, "GROUP:%s\n", sidstr);
642 :
643 : /* Print aces */
644 0 : for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
645 0 : struct security_ace *ace = &sd->dacl->aces[i];
646 0 : fprintf(f, "ACL:");
647 0 : print_ace(cli, f, ace, numeric);
648 0 : fprintf(f, "\n");
649 : }
650 :
651 0 : }
|