libsmb: Remove unused setup_stat_from_stat_ex()
[Samba.git] / source3 / libsmb / libsmb_xattr.c
bloba90234193765c81e134f46d69e95a20abe7c1cb2
1 /*
2 Unix SMB/Netbios implementation.
3 SMB client library implementation
4 Copyright (C) Andrew Tridgell 1998
5 Copyright (C) Richard Sharpe 2000, 2002
6 Copyright (C) John Terpstra 2000
7 Copyright (C) Tom Jansen (Ninja ISD) 2002
8 Copyright (C) Derrell Lipman 2003-2008
9 Copyright (C) Jeremy Allison 2007, 2008
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "libsmb/libsmb.h"
27 #include "libsmbclient.h"
28 #include "libsmb_internal.h"
29 #include "../librpc/gen_ndr/ndr_lsa.h"
30 #include "rpc_client/rpc_client.h"
31 #include "rpc_client/cli_lsarpc.h"
32 #include "../libcli/security/security.h"
33 #include "lib/util/string_wrappers.h"
36 * Find an lsa pipe handle associated with a cli struct.
38 static struct rpc_pipe_client *
39 find_lsa_pipe_hnd(struct cli_state *ipc_cli)
41 struct rpc_pipe_client *pipe_hnd;
43 for (pipe_hnd = ipc_cli->pipe_list;
44 pipe_hnd;
45 pipe_hnd = pipe_hnd->next) {
46 if (ndr_syntax_id_equal(&pipe_hnd->abstract_syntax,
47 &ndr_table_lsarpc.syntax_id)) {
48 return pipe_hnd;
51 return NULL;
55 * Sort ACEs according to the documentation at
56 * http://support.microsoft.com/kb/269175, at least as far as it defines the
57 * order.
60 static int
61 ace_compare(struct security_ace *ace1,
62 struct security_ace *ace2)
64 bool b1;
65 bool b2;
67 /* If the ACEs are equal, we have nothing more to do. */
68 if (security_ace_equal(ace1, ace2)) {
69 return 0;
72 /* Inherited follow non-inherited */
73 b1 = ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
74 b2 = ((ace2->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
75 if (b1 != b2) {
76 return (b1 ? 1 : -1);
80 * What shall we do with AUDITs and ALARMs? It's undefined. We'll
81 * sort them after DENY and ALLOW.
83 b1 = (ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
84 ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
85 ace1->type != SEC_ACE_TYPE_ACCESS_DENIED &&
86 ace1->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
87 b2 = (ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
88 ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
89 ace2->type != SEC_ACE_TYPE_ACCESS_DENIED &&
90 ace2->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
91 if (b1 != b2) {
92 return (b1 ? 1 : -1);
95 /* Allowed ACEs follow denied ACEs */
96 b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
97 ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
98 b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
99 ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
100 if (b1 != b2) {
101 return (b1 ? 1 : -1);
105 * ACEs applying to an entity's object follow those applying to the
106 * entity itself
108 b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
109 ace1->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
110 b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
111 ace2->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
112 if (b1 != b2) {
113 return (b1 ? 1 : -1);
117 * If we get this far, the ACEs are similar as far as the
118 * characteristics we typically care about (those defined by the
119 * referenced MS document). We'll now sort by characteristics that
120 * just seems reasonable.
123 if (ace1->type != ace2->type) {
125 * ace2 and ace1 are reversed here, so that
126 * ACCESS_DENIED_ACE_TYPE (1) sorts before
127 * ACCESS_ALLOWED_ACE_TYPE (0), which is the order you
128 * usually want.
130 return NUMERIC_CMP(ace2->type, ace1->type);
133 if (dom_sid_compare(&ace1->trustee, &ace2->trustee)) {
134 return dom_sid_compare(&ace1->trustee, &ace2->trustee);
137 if (ace1->flags != ace2->flags) {
138 return NUMERIC_CMP(ace1->flags, ace2->flags);
141 if (ace1->access_mask != ace2->access_mask) {
142 return NUMERIC_CMP(ace1->access_mask, ace2->access_mask);
145 if (ace1->size != ace2->size) {
146 return NUMERIC_CMP(ace1->size, ace2->size);
149 return memcmp(ace1, ace2, sizeof(struct security_ace));
153 static void
154 sort_acl(struct security_acl *the_acl)
156 uint32_t i;
157 if (!the_acl) return;
159 TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
161 for (i=1;i<the_acl->num_aces;) {
162 if (security_ace_equal(&the_acl->aces[i-1],
163 &the_acl->aces[i])) {
164 ARRAY_DEL_ELEMENT(
165 the_acl->aces, i, the_acl->num_aces);
166 the_acl->num_aces--;
167 } else {
168 i++;
173 /* convert a SID to a string, either numeric or username/group */
174 static void
175 convert_sid_to_string(struct cli_state *ipc_cli,
176 struct policy_handle *pol,
177 fstring str,
178 bool numeric,
179 struct dom_sid *sid)
181 char **domains = NULL;
182 char **names = NULL;
183 enum lsa_SidType *types = NULL;
184 struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
185 TALLOC_CTX *ctx;
187 sid_to_fstring(str, sid);
189 if (numeric) {
190 return; /* no lookup desired */
193 if (!pipe_hnd) {
194 return;
197 /* Ask LSA to convert the sid to a name */
199 ctx = talloc_stackframe();
201 if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ctx,
202 pol, 1, sid, &domains,
203 &names, &types)) ||
204 !domains || !domains[0] || !names || !names[0]) {
205 TALLOC_FREE(ctx);
206 return;
209 /* Converted OK */
211 fstr_sprintf(str, "%s%s%s",
212 domains[0], lp_winbind_separator(), names[0]);
214 TALLOC_FREE(ctx);
217 /* convert a string to a SID, either numeric or username/group */
218 static bool
219 convert_string_to_sid(struct cli_state *ipc_cli,
220 struct policy_handle *pol,
221 bool numeric,
222 struct dom_sid *sid,
223 const char *str)
225 enum lsa_SidType *types = NULL;
226 struct dom_sid *sids = NULL;
227 bool result = True;
228 TALLOC_CTX *ctx = NULL;
229 struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
231 if (!pipe_hnd) {
232 return False;
235 if (numeric) {
236 if (strncmp(str, "S-", 2) == 0) {
237 return string_to_sid(sid, str);
240 result = False;
241 goto done;
244 ctx = talloc_stackframe();
245 if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ctx,
246 pol, 1, &str,
247 NULL, 1, &sids,
248 &types))) {
249 result = False;
250 goto done;
253 sid_copy(sid, &sids[0]);
254 done:
255 TALLOC_FREE(ctx);
256 return result;
260 /* parse an struct security_ace in the same format as print_ace() */
261 static bool
262 parse_ace(struct cli_state *ipc_cli,
263 struct policy_handle *pol,
264 struct security_ace *ace,
265 bool numeric,
266 char *str)
268 char *p;
269 const char *cp;
270 char *tok;
271 unsigned int atype;
272 unsigned int aflags;
273 unsigned int amask;
274 struct dom_sid sid;
275 uint32_t mask;
276 struct perm_value {
277 const char perm[7];
278 uint32_t mask;
280 size_t i;
281 TALLOC_CTX *frame = talloc_stackframe();
283 /* These values discovered by inspection */
284 static const struct perm_value special_values[] = {
285 { "R", 0x00120089 },
286 { "W", 0x00120116 },
287 { "X", 0x001200a0 },
288 { "D", 0x00010000 },
289 { "P", 0x00040000 },
290 { "O", 0x00080000 },
293 static const struct perm_value standard_values[] = {
294 { "READ", 0x001200a9 },
295 { "CHANGE", 0x001301bf },
296 { "FULL", 0x001f01ff },
299 ZERO_STRUCTP(ace);
300 p = strchr_m(str,':');
301 if (!p) {
302 TALLOC_FREE(frame);
303 return False;
305 *p = '\0';
306 p++;
307 /* Try to parse numeric form */
309 if (sscanf(p, "%u/%u/%u", &atype, &aflags, &amask) == 3 &&
310 convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
311 goto done;
314 /* Try to parse text form */
316 if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
317 TALLOC_FREE(frame);
318 return false;
321 cp = p;
322 if (!next_token_talloc(frame, &cp, &tok, "/")) {
323 TALLOC_FREE(frame);
324 return false;
327 if (strncasecmp_m(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
328 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
329 } else if (strncasecmp_m(tok, "DENIED", strlen("DENIED")) == 0) {
330 atype = SEC_ACE_TYPE_ACCESS_DENIED;
331 } else {
332 TALLOC_FREE(frame);
333 return false;
336 /* Only numeric form accepted for flags at present */
338 if (!(next_token_talloc(frame, &cp, &tok, "/") &&
339 sscanf(tok, "%u", &aflags))) {
340 TALLOC_FREE(frame);
341 return false;
344 if (!next_token_talloc(frame, &cp, &tok, "/")) {
345 TALLOC_FREE(frame);
346 return false;
349 if (strncmp(tok, "0x", 2) == 0) {
350 if (sscanf(tok, "%u", &amask) != 1) {
351 TALLOC_FREE(frame);
352 return false;
354 goto done;
357 for (i = 0; i < ARRAY_SIZE(standard_values); i++) {
358 const struct perm_value *v = &standard_values[i];
359 if (strcmp(tok, v->perm) == 0) {
360 amask = v->mask;
361 goto done;
365 p = tok;
367 while(*p) {
368 bool found = False;
370 for (i = 0; i < ARRAY_SIZE(special_values); i++) {
371 const struct perm_value *v = &special_values[i];
372 if (v->perm[0] == *p) {
373 amask |= v->mask;
374 found = True;
378 if (!found) {
379 TALLOC_FREE(frame);
380 return false;
382 p++;
385 if (*p) {
386 TALLOC_FREE(frame);
387 return false;
390 done:
391 mask = amask;
392 init_sec_ace(ace, &sid, atype, mask, aflags);
393 TALLOC_FREE(frame);
394 return true;
397 /* add an struct security_ace to a list of struct security_aces in a struct security_acl */
398 static bool
399 add_ace(struct security_acl **the_acl,
400 const struct security_ace *ace,
401 TALLOC_CTX *ctx)
403 struct security_acl *acl = *the_acl;
405 if (acl == NULL) {
406 acl = make_sec_acl(ctx, 3, 0, NULL);
407 if (acl == NULL) {
408 return false;
412 if (acl->num_aces == UINT32_MAX) {
413 return false;
415 ADD_TO_ARRAY(
416 acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
417 *the_acl = acl;
418 return True;
422 /* parse a ascii version of a security descriptor */
423 static struct security_descriptor *
424 sec_desc_parse(TALLOC_CTX *ctx,
425 struct cli_state *ipc_cli,
426 struct policy_handle *pol,
427 bool numeric,
428 const char *str)
430 const char *p = str;
431 char *tok;
432 struct security_descriptor *ret = NULL;
433 size_t sd_size;
434 struct dom_sid owner_sid = { .num_auths = 0 };
435 struct dom_sid group_sid = { .num_auths = 0 };
436 bool have_owner = false, have_group = false;
437 struct security_acl *dacl=NULL;
438 int revision=1;
440 while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
442 if (strncasecmp_m(tok,"REVISION:", 9) == 0) {
443 revision = strtol(tok+9, NULL, 16);
444 continue;
447 if (strncasecmp_m(tok,"OWNER:", 6) == 0) {
448 if (have_owner) {
449 DEBUG(5,("OWNER specified more than once!\n"));
450 goto done;
452 if (!convert_string_to_sid(ipc_cli, pol,
453 numeric,
454 &owner_sid, tok+6)) {
455 DEBUG(5, ("Failed to parse owner sid\n"));
456 goto done;
458 have_owner = true;
459 continue;
462 if (strncasecmp_m(tok,"OWNER+:", 7) == 0) {
463 if (have_owner) {
464 DEBUG(5,("OWNER specified more than once!\n"));
465 goto done;
467 if (!convert_string_to_sid(ipc_cli, pol,
468 False,
469 &owner_sid, tok+7)) {
470 DEBUG(5, ("Failed to parse owner sid\n"));
471 goto done;
473 have_owner = true;
474 continue;
477 if (strncasecmp_m(tok,"GROUP:", 6) == 0) {
478 if (have_group) {
479 DEBUG(5,("GROUP specified more than once!\n"));
480 goto done;
482 if (!convert_string_to_sid(ipc_cli, pol,
483 numeric,
484 &group_sid, tok+6)) {
485 DEBUG(5, ("Failed to parse group sid\n"));
486 goto done;
488 have_group = true;
489 continue;
492 if (strncasecmp_m(tok,"GROUP+:", 7) == 0) {
493 if (have_group) {
494 DEBUG(5,("GROUP specified more than once!\n"));
495 goto done;
497 if (!convert_string_to_sid(ipc_cli, pol,
498 False,
499 &group_sid, tok+6)) {
500 DEBUG(5, ("Failed to parse group sid\n"));
501 goto done;
503 have_group = true;
504 continue;
507 if (strncasecmp_m(tok,"ACL:", 4) == 0) {
508 struct security_ace ace;
509 if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
510 DEBUG(5, ("Failed to parse ACL %s\n", tok));
511 goto done;
513 if(!add_ace(&dacl, &ace, ctx)) {
514 DEBUG(5, ("Failed to add ACL %s\n", tok));
515 goto done;
517 continue;
520 if (strncasecmp_m(tok,"ACL+:", 5) == 0) {
521 struct security_ace ace;
522 if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
523 DEBUG(5, ("Failed to parse ACL %s\n", tok));
524 goto done;
526 if(!add_ace(&dacl, &ace, ctx)) {
527 DEBUG(5, ("Failed to add ACL %s\n", tok));
528 goto done;
530 continue;
533 DEBUG(5, ("Failed to parse security descriptor\n"));
534 goto done;
537 ret = make_sec_desc(
538 ctx,
539 revision,
540 SEC_DESC_SELF_RELATIVE,
541 have_owner ? &owner_sid : NULL,
542 have_group ? &group_sid : NULL,
543 NULL,
544 dacl,
545 &sd_size);
547 done:
548 return ret;
552 /* Obtain the current dos attributes */
553 static struct DOS_ATTR_DESC *
554 dos_attr_query(SMBCCTX *context,
555 TALLOC_CTX *ctx,
556 const char *filename,
557 SMBCSRV *srv)
559 struct stat sb = {0};
560 struct DOS_ATTR_DESC *ret = NULL;
561 NTSTATUS status;
563 ret = talloc(ctx, struct DOS_ATTR_DESC);
564 if (!ret) {
565 errno = ENOMEM;
566 return NULL;
569 /* Obtain the DOS attributes */
570 status = SMBC_getatr(context, srv, filename, &sb);
571 if (!NT_STATUS_IS_OK(status)) {
572 DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
573 TALLOC_FREE(ret);
574 errno = cli_status_to_errno(status);
575 return NULL;
578 ret->mode = sb.st_mode;
579 ret->size = sb.st_size;
580 ret->create_time = sb.st_ctime;
581 ret->access_time = sb.st_atime;
582 ret->write_time = sb.st_mtime;
583 ret->change_time = sb.st_mtime;
584 ret->inode = sb.st_ino;
586 return ret;
590 /* parse a ascii version of a security descriptor */
591 static void
592 dos_attr_parse(SMBCCTX *context,
593 struct DOS_ATTR_DESC *dad,
594 SMBCSRV *srv,
595 char *str)
597 int n;
598 const char *p = str;
599 char *tok = NULL;
600 TALLOC_CTX *frame = NULL;
601 struct {
602 const char * create_time_attr;
603 const char * access_time_attr;
604 const char * write_time_attr;
605 const char * change_time_attr;
606 } attr_strings;
608 /* Determine whether to use old-style or new-style attribute names */
609 if (context->internal->full_time_names) {
610 /* new-style names */
611 attr_strings.create_time_attr = "CREATE_TIME";
612 attr_strings.access_time_attr = "ACCESS_TIME";
613 attr_strings.write_time_attr = "WRITE_TIME";
614 attr_strings.change_time_attr = "CHANGE_TIME";
615 } else {
616 /* old-style names */
617 attr_strings.create_time_attr = NULL;
618 attr_strings.access_time_attr = "A_TIME";
619 attr_strings.write_time_attr = "M_TIME";
620 attr_strings.change_time_attr = "C_TIME";
623 /* if this is to set the entire ACL... */
624 if (*str == '*') {
625 /* ... then increment past the first colon if there is one */
626 if ((p = strchr(str, ':')) != NULL) {
627 ++p;
628 } else {
629 p = str;
633 frame = talloc_stackframe();
634 while (next_token_talloc(frame, &p, &tok, "\t,\r\n")) {
635 if (strncasecmp_m(tok, "MODE:", 5) == 0) {
636 long request = strtol(tok+5, NULL, 16);
637 if (request == 0) {
638 dad->mode =
639 (dad->mode & FILE_ATTRIBUTE_DIRECTORY)
640 ? FILE_ATTRIBUTE_DIRECTORY
641 : FILE_ATTRIBUTE_NORMAL;
642 } else {
643 dad->mode = request;
645 continue;
648 if (strncasecmp_m(tok, "SIZE:", 5) == 0) {
649 dad->size = (off_t)atof(tok+5);
650 continue;
653 n = strlen(attr_strings.access_time_attr);
654 if (strncasecmp_m(tok, attr_strings.access_time_attr, n) == 0) {
655 dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
656 continue;
659 n = strlen(attr_strings.change_time_attr);
660 if (strncasecmp_m(tok, attr_strings.change_time_attr, n) == 0) {
661 dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
662 continue;
665 n = strlen(attr_strings.write_time_attr);
666 if (strncasecmp_m(tok, attr_strings.write_time_attr, n) == 0) {
667 dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
668 continue;
671 if (attr_strings.create_time_attr != NULL) {
672 n = strlen(attr_strings.create_time_attr);
673 if (strncasecmp_m(tok, attr_strings.create_time_attr,
674 n) == 0) {
675 dad->create_time = (time_t)strtol(tok+n+1,
676 NULL, 10);
677 continue;
681 if (strncasecmp_m(tok, "INODE:", 6) == 0) {
682 dad->inode = (SMB_INO_T)atof(tok+6);
683 continue;
686 TALLOC_FREE(frame);
689 /*****************************************************
690 Retrieve the acls for a file.
691 *******************************************************/
693 static int
694 cacl_get(SMBCCTX *context,
695 TALLOC_CTX *ctx,
696 SMBCSRV *srv,
697 struct cli_state *ipc_cli,
698 struct policy_handle *pol,
699 const char *filename,
700 const char *attr_name,
701 char *buf,
702 int bufsize)
704 uint32_t i;
705 int n = 0;
706 int n_used;
707 bool all;
708 bool all_nt;
709 bool all_nt_acls;
710 bool all_dos;
711 bool some_nt;
712 bool some_dos;
713 bool exclude_nt_revision = False;
714 bool exclude_nt_owner = False;
715 bool exclude_nt_group = False;
716 bool exclude_nt_acl = False;
717 bool exclude_dos_mode = False;
718 bool exclude_dos_size = False;
719 bool exclude_dos_create_time = False;
720 bool exclude_dos_access_time = False;
721 bool exclude_dos_write_time = False;
722 bool exclude_dos_change_time = False;
723 bool exclude_dos_inode = False;
724 bool numeric = True;
725 bool determine_size = (bufsize == 0);
726 uint16_t fnum;
727 struct security_descriptor *sd;
728 fstring sidstr;
729 fstring name_sandbox;
730 char *name;
731 char *pExclude;
732 char *p;
733 struct cli_state *cli = srv->cli;
734 struct {
735 const char * create_time_attr;
736 const char * access_time_attr;
737 const char * write_time_attr;
738 const char * change_time_attr;
739 } attr_strings;
740 struct {
741 const char * create_time_attr;
742 const char * access_time_attr;
743 const char * write_time_attr;
744 const char * change_time_attr;
745 } excl_attr_strings;
747 /* Determine whether to use old-style or new-style attribute names */
748 if (context->internal->full_time_names) {
749 /* new-style names */
750 attr_strings.create_time_attr = "CREATE_TIME";
751 attr_strings.access_time_attr = "ACCESS_TIME";
752 attr_strings.write_time_attr = "WRITE_TIME";
753 attr_strings.change_time_attr = "CHANGE_TIME";
755 excl_attr_strings.create_time_attr = "CREATE_TIME";
756 excl_attr_strings.access_time_attr = "ACCESS_TIME";
757 excl_attr_strings.write_time_attr = "WRITE_TIME";
758 excl_attr_strings.change_time_attr = "CHANGE_TIME";
759 } else {
760 /* old-style names */
761 attr_strings.create_time_attr = NULL;
762 attr_strings.access_time_attr = "A_TIME";
763 attr_strings.write_time_attr = "M_TIME";
764 attr_strings.change_time_attr = "C_TIME";
766 excl_attr_strings.create_time_attr = NULL;
767 excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
768 excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
769 excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
772 /* Copy name so we can strip off exclusions (if any are specified) */
773 fstrcpy(name_sandbox, attr_name);
775 /* Ensure name is null terminated */
776 name_sandbox[sizeof(name_sandbox) - 1] = '\0';
778 /* Play in the sandbox */
779 name = name_sandbox;
781 /* If there are any exclusions, point to them and mask them from name */
782 if ((pExclude = strchr(name, '!')) != NULL)
784 *pExclude++ = '\0';
787 all = (strncasecmp_m(name, "system.*", 8) == 0);
788 all_nt = (strncasecmp_m(name, "system.nt_sec_desc.*", 20) == 0);
789 all_nt_acls = (strncasecmp_m(name, "system.nt_sec_desc.acl.*", 24) == 0);
790 all_dos = (strncasecmp_m(name, "system.dos_attr.*", 17) == 0);
791 some_nt = (strncasecmp_m(name, "system.nt_sec_desc.", 19) == 0);
792 some_dos = (strncasecmp_m(name, "system.dos_attr.", 16) == 0);
793 numeric = (* (name + strlen(name) - 1) != '+');
795 /* Look for exclusions from "all" requests */
796 if (all || all_nt || all_dos) {
797 /* Exclusions are delimited by '!' */
798 for (;
799 pExclude != NULL;
800 pExclude = (p == NULL ? NULL : p + 1)) {
802 /* Find end of this exclusion name */
803 if ((p = strchr(pExclude, '!')) != NULL)
805 *p = '\0';
808 /* Which exclusion name is this? */
809 if (strcasecmp_m(pExclude,
810 "nt_sec_desc.revision") == 0) {
811 exclude_nt_revision = True;
813 else if (strcasecmp_m(pExclude,
814 "nt_sec_desc.owner") == 0) {
815 exclude_nt_owner = True;
817 else if (strcasecmp_m(pExclude,
818 "nt_sec_desc.group") == 0) {
819 exclude_nt_group = True;
821 else if (strcasecmp_m(pExclude,
822 "nt_sec_desc.acl") == 0) {
823 exclude_nt_acl = True;
825 else if (strcasecmp_m(pExclude,
826 "dos_attr.mode") == 0) {
827 exclude_dos_mode = True;
829 else if (strcasecmp_m(pExclude,
830 "dos_attr.size") == 0) {
831 exclude_dos_size = True;
833 else if (excl_attr_strings.create_time_attr != NULL &&
834 strcasecmp_m(pExclude,
835 excl_attr_strings.change_time_attr) == 0) {
836 exclude_dos_create_time = True;
838 else if (strcasecmp_m(pExclude,
839 excl_attr_strings.access_time_attr) == 0) {
840 exclude_dos_access_time = True;
842 else if (strcasecmp_m(pExclude,
843 excl_attr_strings.write_time_attr) == 0) {
844 exclude_dos_write_time = True;
846 else if (strcasecmp_m(pExclude,
847 excl_attr_strings.change_time_attr) == 0) {
848 exclude_dos_change_time = True;
850 else if (strcasecmp_m(pExclude, "dos_attr.inode") == 0) {
851 exclude_dos_inode = True;
853 else {
854 DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
855 pExclude));
856 errno = ENOATTR;
857 return -1;
862 n_used = 0;
865 * If we are (possibly) talking to an NT or new system and some NT
866 * attributes have been requested...
868 if (ipc_cli && (all || some_nt || all_nt_acls)) {
869 char *targetpath = NULL;
870 struct cli_state *targetcli = NULL;
871 struct cli_credentials *creds = NULL;
872 NTSTATUS status;
874 /* Point to the portion after "system.nt_sec_desc." */
875 name += 19; /* if (all) this will be invalid but unused */
877 creds = context->internal->creds;
879 status = cli_resolve_path(
880 ctx, "",
881 creds,
882 cli, filename, &targetcli, &targetpath);
883 if (!NT_STATUS_IS_OK(status)) {
884 DEBUG(5, ("cacl_get Could not resolve %s\n",
885 filename));
886 errno = ENOENT;
887 return -1;
890 /* ... then obtain any NT attributes which were requested */
891 status = cli_ntcreate(
892 targetcli, /* cli */
893 targetpath, /* fname */
894 0, /* CreatFlags */
895 READ_CONTROL_ACCESS, /* DesiredAccess */
896 0, /* FileAttributes */
897 FILE_SHARE_READ|
898 FILE_SHARE_WRITE, /* ShareAccess */
899 FILE_OPEN, /* CreateDisposition */
900 0x0, /* CreateOptions */
901 0x0, /* SecurityFlags */
902 &fnum, /* pfid */
903 NULL); /* cr */
904 if (!NT_STATUS_IS_OK(status)) {
905 DEBUG(5, ("cacl_get failed to open %s: %s\n",
906 targetpath, nt_errstr(status)));
907 errno = cli_status_to_errno(status);
908 return -1;
911 status = cli_query_secdesc(targetcli, fnum, ctx, &sd);
912 if (!NT_STATUS_IS_OK(status)) {
913 DEBUG(5,("cacl_get Failed to query old descriptor "
914 "of %s: %s\n",
915 targetpath, nt_errstr(status)));
916 errno = cli_status_to_errno(status);
917 return -1;
920 cli_close(targetcli, fnum);
922 if (! exclude_nt_revision) {
923 if (all || all_nt) {
924 if (determine_size) {
925 p = talloc_asprintf(ctx,
926 "REVISION:%d",
927 sd->revision);
928 if (!p) {
929 errno = ENOMEM;
930 return -1;
932 n = strlen(p);
933 } else {
934 n = snprintf(buf, bufsize,
935 "REVISION:%d",
936 sd->revision);
938 } else if (strcasecmp_m(name, "revision") == 0) {
939 if (determine_size) {
940 p = talloc_asprintf(ctx, "%d",
941 sd->revision);
942 if (!p) {
943 errno = ENOMEM;
944 return -1;
946 n = strlen(p);
947 } else {
948 n = snprintf(buf, bufsize, "%d",
949 sd->revision);
953 if (!determine_size && n > bufsize) {
954 errno = ERANGE;
955 return -1;
957 buf += n;
958 n_used += n;
959 bufsize -= n;
960 n = 0;
963 if (! exclude_nt_owner) {
964 /* Get owner and group sid */
965 if (sd->owner_sid) {
966 convert_sid_to_string(ipc_cli, pol,
967 sidstr,
968 numeric,
969 sd->owner_sid);
970 } else {
971 fstrcpy(sidstr, "");
974 if (all || all_nt) {
975 if (determine_size) {
976 p = talloc_asprintf(ctx, ",OWNER:%s",
977 sidstr);
978 if (!p) {
979 errno = ENOMEM;
980 return -1;
982 n = strlen(p);
983 } else if (sidstr[0] != '\0') {
984 n = snprintf(buf, bufsize,
985 ",OWNER:%s", sidstr);
987 } else if (strncasecmp_m(name, "owner", 5) == 0) {
988 if (determine_size) {
989 p = talloc_asprintf(ctx, "%s", sidstr);
990 if (!p) {
991 errno = ENOMEM;
992 return -1;
994 n = strlen(p);
995 } else {
996 n = snprintf(buf, bufsize, "%s",
997 sidstr);
1001 if (!determine_size && n > bufsize) {
1002 errno = ERANGE;
1003 return -1;
1005 buf += n;
1006 n_used += n;
1007 bufsize -= n;
1008 n = 0;
1011 if (! exclude_nt_group) {
1012 if (sd->group_sid) {
1013 convert_sid_to_string(ipc_cli, pol,
1014 sidstr, numeric,
1015 sd->group_sid);
1016 } else {
1017 fstrcpy(sidstr, "");
1020 if (all || all_nt) {
1021 if (determine_size) {
1022 p = talloc_asprintf(ctx, ",GROUP:%s",
1023 sidstr);
1024 if (!p) {
1025 errno = ENOMEM;
1026 return -1;
1028 n = strlen(p);
1029 } else if (sidstr[0] != '\0') {
1030 n = snprintf(buf, bufsize,
1031 ",GROUP:%s", sidstr);
1033 } else if (strncasecmp_m(name, "group", 5) == 0) {
1034 if (determine_size) {
1035 p = talloc_asprintf(ctx, "%s", sidstr);
1036 if (!p) {
1037 errno = ENOMEM;
1038 return -1;
1040 n = strlen(p);
1041 } else {
1042 n = snprintf(buf, bufsize,
1043 "%s", sidstr);
1047 if (!determine_size && n > bufsize) {
1048 errno = ERANGE;
1049 return -1;
1051 buf += n;
1052 n_used += n;
1053 bufsize -= n;
1054 n = 0;
1057 if (! exclude_nt_acl) {
1058 /* Add aces to value buffer */
1059 for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
1061 struct security_ace *ace = &sd->dacl->aces[i];
1062 convert_sid_to_string(ipc_cli, pol,
1063 sidstr, numeric,
1064 &ace->trustee);
1066 if (all || all_nt) {
1067 if (determine_size) {
1068 p = talloc_asprintf(
1069 ctx,
1070 ",ACL:"
1071 "%s:%d/%d/0x%08x",
1072 sidstr,
1073 ace->type,
1074 ace->flags,
1075 ace->access_mask);
1076 if (!p) {
1077 errno = ENOMEM;
1078 return -1;
1080 n = strlen(p);
1081 } else {
1082 n = snprintf(
1083 buf, bufsize,
1084 ",ACL:%s:%d/%d/0x%08x",
1085 sidstr,
1086 ace->type,
1087 ace->flags,
1088 ace->access_mask);
1090 } else if ((strncasecmp_m(name, "acl", 3) == 0 &&
1091 strcasecmp_m(name+3, sidstr) == 0) ||
1092 (strncasecmp_m(name, "acl+", 4) == 0 &&
1093 strcasecmp_m(name+4, sidstr) == 0)) {
1094 if (determine_size) {
1095 p = talloc_asprintf(
1096 ctx,
1097 "%d/%d/0x%08x",
1098 ace->type,
1099 ace->flags,
1100 ace->access_mask);
1101 if (!p) {
1102 errno = ENOMEM;
1103 return -1;
1105 n = strlen(p);
1106 } else {
1107 n = snprintf(buf, bufsize,
1108 "%d/%d/0x%08x",
1109 ace->type,
1110 ace->flags,
1111 ace->access_mask);
1113 } else if (all_nt_acls) {
1114 if (determine_size) {
1115 p = talloc_asprintf(
1116 ctx,
1117 "%s%s:%d/%d/0x%08x",
1118 i ? "," : "",
1119 sidstr,
1120 ace->type,
1121 ace->flags,
1122 ace->access_mask);
1123 if (!p) {
1124 errno = ENOMEM;
1125 return -1;
1127 n = strlen(p);
1128 } else {
1129 n = snprintf(buf, bufsize,
1130 "%s%s:%d/%d/0x%08x",
1131 i ? "," : "",
1132 sidstr,
1133 ace->type,
1134 ace->flags,
1135 ace->access_mask);
1138 if (!determine_size && n > bufsize) {
1139 errno = ERANGE;
1140 return -1;
1142 buf += n;
1143 n_used += n;
1144 bufsize -= n;
1145 n = 0;
1149 /* Restore name pointer to its original value */
1150 name -= 19;
1153 if (all || some_dos) {
1154 struct stat sb = {0};
1155 time_t create_time = (time_t)0;
1156 time_t write_time = (time_t)0;
1157 time_t access_time = (time_t)0;
1158 time_t change_time = (time_t)0;
1159 off_t size = 0;
1160 uint16_t mode = 0;
1161 SMB_INO_T ino = 0;
1162 NTSTATUS status;
1164 /* Point to the portion after "system.dos_attr." */
1165 name += 16; /* if (all) this will be invalid but unused */
1167 /* Obtain the DOS attributes */
1168 status = SMBC_getatr(context, srv, filename, &sb);
1169 if (!NT_STATUS_IS_OK(status)) {
1170 errno = cli_status_to_errno(status);
1171 return -1;
1174 create_time = sb.st_ctime;
1175 access_time = sb.st_atime;
1176 write_time = sb.st_mtime;
1177 change_time = sb.st_mtime;
1178 size = sb.st_size;
1179 mode = sb.st_mode;
1180 ino = sb.st_ino;
1182 if (! exclude_dos_mode) {
1183 if (all || all_dos) {
1184 if (determine_size) {
1185 p = talloc_asprintf(ctx,
1186 "%sMODE:0x%x",
1187 (ipc_cli &&
1188 (all || some_nt)
1189 ? ","
1190 : ""),
1191 mode);
1192 if (!p) {
1193 errno = ENOMEM;
1194 return -1;
1196 n = strlen(p);
1197 } else {
1198 n = snprintf(buf, bufsize,
1199 "%sMODE:0x%x",
1200 (ipc_cli &&
1201 (all || some_nt)
1202 ? ","
1203 : ""),
1204 mode);
1206 } else if (strcasecmp_m(name, "mode") == 0) {
1207 if (determine_size) {
1208 p = talloc_asprintf(ctx, "0x%x", mode);
1209 if (!p) {
1210 errno = ENOMEM;
1211 return -1;
1213 n = strlen(p);
1214 } else {
1215 n = snprintf(buf, bufsize,
1216 "0x%x", mode);
1220 if (!determine_size && n > bufsize) {
1221 errno = ERANGE;
1222 return -1;
1224 buf += n;
1225 n_used += n;
1226 bufsize -= n;
1227 n = 0;
1230 if (! exclude_dos_size) {
1231 if (all || all_dos) {
1232 if (determine_size) {
1233 p = talloc_asprintf(
1234 ctx,
1235 ",SIZE:%.0f",
1236 (double)size);
1237 if (!p) {
1238 errno = ENOMEM;
1239 return -1;
1241 n = strlen(p);
1242 } else {
1243 n = snprintf(buf, bufsize,
1244 ",SIZE:%.0f",
1245 (double)size);
1247 } else if (strcasecmp_m(name, "size") == 0) {
1248 if (determine_size) {
1249 p = talloc_asprintf(
1250 ctx,
1251 "%.0f",
1252 (double)size);
1253 if (!p) {
1254 errno = ENOMEM;
1255 return -1;
1257 n = strlen(p);
1258 } else {
1259 n = snprintf(buf, bufsize,
1260 "%.0f",
1261 (double)size);
1265 if (!determine_size && n > bufsize) {
1266 errno = ERANGE;
1267 return -1;
1269 buf += n;
1270 n_used += n;
1271 bufsize -= n;
1272 n = 0;
1275 if (! exclude_dos_create_time &&
1276 attr_strings.create_time_attr != NULL) {
1277 if (all || all_dos) {
1278 if (determine_size) {
1279 p = talloc_asprintf(ctx,
1280 ",%s:%lu",
1281 attr_strings.create_time_attr,
1282 (unsigned long) create_time);
1283 if (!p) {
1284 errno = ENOMEM;
1285 return -1;
1287 n = strlen(p);
1288 } else {
1289 n = snprintf(buf, bufsize,
1290 ",%s:%lu",
1291 attr_strings.create_time_attr,
1292 (unsigned long) create_time);
1294 } else if (strcasecmp_m(name, attr_strings.create_time_attr) == 0) {
1295 if (determine_size) {
1296 p = talloc_asprintf(ctx, "%lu", (unsigned long) create_time);
1297 if (!p) {
1298 errno = ENOMEM;
1299 return -1;
1301 n = strlen(p);
1302 } else {
1303 n = snprintf(buf, bufsize,
1304 "%lu", (unsigned long) create_time);
1308 if (!determine_size && n > bufsize) {
1309 errno = ERANGE;
1310 return -1;
1312 buf += n;
1313 n_used += n;
1314 bufsize -= n;
1315 n = 0;
1318 if (! exclude_dos_access_time) {
1319 if (all || all_dos) {
1320 if (determine_size) {
1321 p = talloc_asprintf(ctx,
1322 ",%s:%lu",
1323 attr_strings.access_time_attr,
1324 (unsigned long) access_time);
1325 if (!p) {
1326 errno = ENOMEM;
1327 return -1;
1329 n = strlen(p);
1330 } else {
1331 n = snprintf(buf, bufsize,
1332 ",%s:%lu",
1333 attr_strings.access_time_attr,
1334 (unsigned long) access_time);
1336 } else if (strcasecmp_m(name, attr_strings.access_time_attr) == 0) {
1337 if (determine_size) {
1338 p = talloc_asprintf(ctx, "%lu", (unsigned long) access_time);
1339 if (!p) {
1340 errno = ENOMEM;
1341 return -1;
1343 n = strlen(p);
1344 } else {
1345 n = snprintf(buf, bufsize,
1346 "%lu", (unsigned long) access_time);
1350 if (!determine_size && n > bufsize) {
1351 errno = ERANGE;
1352 return -1;
1354 buf += n;
1355 n_used += n;
1356 bufsize -= n;
1357 n = 0;
1360 if (! exclude_dos_write_time) {
1361 if (all || all_dos) {
1362 if (determine_size) {
1363 p = talloc_asprintf(ctx,
1364 ",%s:%lu",
1365 attr_strings.write_time_attr,
1366 (unsigned long) write_time);
1367 if (!p) {
1368 errno = ENOMEM;
1369 return -1;
1371 n = strlen(p);
1372 } else {
1373 n = snprintf(buf, bufsize,
1374 ",%s:%lu",
1375 attr_strings.write_time_attr,
1376 (unsigned long) write_time);
1378 } else if (strcasecmp_m(name, attr_strings.write_time_attr) == 0) {
1379 if (determine_size) {
1380 p = talloc_asprintf(ctx, "%lu", (unsigned long) write_time);
1381 if (!p) {
1382 errno = ENOMEM;
1383 return -1;
1385 n = strlen(p);
1386 } else {
1387 n = snprintf(buf, bufsize,
1388 "%lu", (unsigned long) write_time);
1392 if (!determine_size && n > bufsize) {
1393 errno = ERANGE;
1394 return -1;
1396 buf += n;
1397 n_used += n;
1398 bufsize -= n;
1399 n = 0;
1402 if (! exclude_dos_change_time) {
1403 if (all || all_dos) {
1404 if (determine_size) {
1405 p = talloc_asprintf(ctx,
1406 ",%s:%lu",
1407 attr_strings.change_time_attr,
1408 (unsigned long) change_time);
1409 if (!p) {
1410 errno = ENOMEM;
1411 return -1;
1413 n = strlen(p);
1414 } else {
1415 n = snprintf(buf, bufsize,
1416 ",%s:%lu",
1417 attr_strings.change_time_attr,
1418 (unsigned long) change_time);
1420 } else if (strcasecmp_m(name, attr_strings.change_time_attr) == 0) {
1421 if (determine_size) {
1422 p = talloc_asprintf(ctx, "%lu", (unsigned long) change_time);
1423 if (!p) {
1424 errno = ENOMEM;
1425 return -1;
1427 n = strlen(p);
1428 } else {
1429 n = snprintf(buf, bufsize,
1430 "%lu", (unsigned long) change_time);
1434 if (!determine_size && n > bufsize) {
1435 errno = ERANGE;
1436 return -1;
1438 buf += n;
1439 n_used += n;
1440 bufsize -= n;
1441 n = 0;
1444 if (! exclude_dos_inode) {
1445 if (all || all_dos) {
1446 if (determine_size) {
1447 p = talloc_asprintf(
1448 ctx,
1449 ",INODE:%.0f",
1450 (double)ino);
1451 if (!p) {
1452 errno = ENOMEM;
1453 return -1;
1455 n = strlen(p);
1456 } else {
1457 n = snprintf(buf, bufsize,
1458 ",INODE:%.0f",
1459 (double) ino);
1461 } else if (strcasecmp_m(name, "inode") == 0) {
1462 if (determine_size) {
1463 p = talloc_asprintf(
1464 ctx,
1465 "%.0f",
1466 (double) ino);
1467 if (!p) {
1468 errno = ENOMEM;
1469 return -1;
1471 n = strlen(p);
1472 } else {
1473 n = snprintf(buf, bufsize,
1474 "%.0f",
1475 (double) ino);
1479 if (!determine_size && n > bufsize) {
1480 errno = ERANGE;
1481 return -1;
1483 buf += n;
1484 n_used += n;
1485 bufsize -= n;
1486 n = 0;
1489 /* Restore name pointer to its original value */
1490 name -= 16;
1493 if (n_used == 0) {
1494 errno = ENOATTR;
1495 return -1;
1498 return n_used;
1501 /*****************************************************
1502 set the ACLs on a file given an ascii description
1503 *******************************************************/
1504 static int
1505 cacl_set(SMBCCTX *context,
1506 TALLOC_CTX *ctx,
1507 struct cli_state *cli,
1508 struct cli_state *ipc_cli,
1509 struct policy_handle *pol,
1510 const char *filename,
1511 char *the_acl,
1512 int mode,
1513 int flags)
1515 uint16_t fnum = (uint16_t)-1;
1516 int err = 0;
1517 struct security_descriptor *sd = NULL, *old;
1518 struct security_acl *dacl = NULL;
1519 struct dom_sid *owner_sid = NULL;
1520 struct dom_sid *group_sid = NULL;
1521 uint32_t i, j;
1522 size_t sd_size;
1523 int ret = 0;
1524 char *p;
1525 bool numeric = True;
1526 char *targetpath = NULL;
1527 struct cli_state *targetcli = NULL;
1528 struct cli_credentials *creds = NULL;
1529 NTSTATUS status;
1531 /* the_acl will be null for REMOVE_ALL operations */
1532 if (the_acl) {
1533 numeric = ((p = strchr(the_acl, ':')) != NULL &&
1534 p > the_acl &&
1535 p[-1] != '+');
1537 /* if this is to set the entire ACL... */
1538 if (*the_acl == '*') {
1539 /* ... then increment past the first colon */
1540 the_acl = p + 1;
1543 sd = sec_desc_parse(ctx, ipc_cli, pol, numeric, the_acl);
1544 if (!sd) {
1545 errno = EINVAL;
1546 return -1;
1550 /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
1551 that doesn't deref sd */
1553 if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
1554 errno = EINVAL;
1555 return -1;
1558 creds = context->internal->creds;
1560 status = cli_resolve_path(ctx, "",
1561 creds,
1562 cli, filename, &targetcli, &targetpath);
1563 if (!NT_STATUS_IS_OK(status)) {
1564 DEBUG(5,("cacl_set: Could not resolve %s\n", filename));
1565 errno = ENOENT;
1566 return -1;
1569 /* The desired access below is the only one I could find that works
1570 with NT4, W2KP and Samba */
1572 status = cli_ntcreate(
1573 targetcli, /* cli */
1574 targetpath, /* fname */
1575 0, /* CreatFlags */
1576 READ_CONTROL_ACCESS, /* DesiredAccess */
1577 0, /* FileAttributes */
1578 FILE_SHARE_READ|
1579 FILE_SHARE_WRITE, /* ShareAccess */
1580 FILE_OPEN, /* CreateDisposition */
1581 0x0, /* CreateOptions */
1582 0x0, /* SecurityFlags */
1583 &fnum, /* pfid */
1584 NULL); /* cr */
1585 if (!NT_STATUS_IS_OK(status)) {
1586 DEBUG(5, ("cacl_set failed to open %s: %s\n",
1587 targetpath, nt_errstr(status)));
1588 errno = 0;
1589 return -1;
1592 status = cli_query_secdesc(targetcli, fnum, ctx, &old);
1593 if (!NT_STATUS_IS_OK(status)) {
1594 DEBUG(5,("cacl_set Failed to query old descriptor of %s: %s\n",
1595 targetpath, nt_errstr(status)));
1596 errno = 0;
1597 return -1;
1600 cli_close(targetcli, fnum);
1602 switch (mode) {
1603 case SMBC_XATTR_MODE_REMOVE_ALL:
1604 old->dacl->num_aces = 0;
1605 dacl = old->dacl;
1606 break;
1608 case SMBC_XATTR_MODE_REMOVE:
1609 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
1610 bool found = False;
1612 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
1613 if (security_ace_equal(&sd->dacl->aces[i],
1614 &old->dacl->aces[j])) {
1615 uint32_t k;
1616 for (k=j; k<old->dacl->num_aces-1;k++) {
1617 old->dacl->aces[k] =
1618 old->dacl->aces[k+1];
1620 old->dacl->num_aces--;
1621 found = True;
1622 dacl = old->dacl;
1623 break;
1627 if (!found) {
1628 err = ENOATTR;
1629 ret = -1;
1630 goto failed;
1633 break;
1635 case SMBC_XATTR_MODE_ADD:
1636 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
1637 bool found = False;
1639 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
1640 if (dom_sid_equal(&sd->dacl->aces[i].trustee,
1641 &old->dacl->aces[j].trustee)) {
1642 if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
1643 err = EEXIST;
1644 ret = -1;
1645 goto failed;
1647 old->dacl->aces[j] = sd->dacl->aces[i];
1648 ret = -1;
1649 found = True;
1653 if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
1654 err = ENOATTR;
1655 ret = -1;
1656 goto failed;
1659 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
1660 add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
1663 dacl = old->dacl;
1664 break;
1666 case SMBC_XATTR_MODE_SET:
1667 old = sd;
1668 owner_sid = old->owner_sid;
1669 group_sid = old->group_sid;
1670 dacl = old->dacl;
1671 break;
1673 case SMBC_XATTR_MODE_CHOWN:
1674 owner_sid = sd->owner_sid;
1675 break;
1677 case SMBC_XATTR_MODE_CHGRP:
1678 group_sid = sd->group_sid;
1679 break;
1682 /* Denied ACE entries must come before allowed ones */
1683 sort_acl(old->dacl);
1685 /* Create new security descriptor and set it */
1686 sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
1687 owner_sid, group_sid, NULL, dacl, &sd_size);
1689 status = cli_ntcreate(targetcli, targetpath, 0,
1690 WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS, 0,
1691 FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
1692 0x0, 0x0, &fnum, NULL);
1693 if (!NT_STATUS_IS_OK(status)) {
1694 DEBUG(5, ("cacl_set failed to open %s: %s\n",
1695 targetpath, nt_errstr(status)));
1696 errno = 0;
1697 return -1;
1700 status = cli_set_secdesc(targetcli, fnum, sd);
1701 if (!NT_STATUS_IS_OK(status)) {
1702 DEBUG(5, ("ERROR: secdesc set failed: %s\n",
1703 nt_errstr(status)));
1704 ret = -1;
1707 /* Clean up */
1709 failed:
1710 cli_close(targetcli, fnum);
1712 if (err != 0) {
1713 errno = err;
1716 return ret;
1721 SMBC_setxattr_ctx(SMBCCTX *context,
1722 const char *fname,
1723 const char *name,
1724 const void *value,
1725 size_t size,
1726 int flags)
1728 int ret;
1729 int ret2;
1730 SMBCSRV *srv = NULL;
1731 SMBCSRV *ipc_srv = NULL;
1732 char *server = NULL;
1733 char *share = NULL;
1734 char *user = NULL;
1735 char *password = NULL;
1736 char *workgroup = NULL;
1737 char *path = NULL;
1738 struct DOS_ATTR_DESC *dad = NULL;
1739 struct {
1740 const char * create_time_attr;
1741 const char * access_time_attr;
1742 const char * write_time_attr;
1743 const char * change_time_attr;
1744 } attr_strings;
1745 uint16_t port = 0;
1746 TALLOC_CTX *frame = talloc_stackframe();
1748 if (!context || !context->internal->initialized) {
1749 errno = EINVAL; /* Best I can think of ... */
1750 TALLOC_FREE(frame);
1751 return -1;
1754 if (!fname) {
1755 errno = EINVAL;
1756 TALLOC_FREE(frame);
1757 return -1;
1760 DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
1761 fname, name, (int) size, (const char*)value));
1763 if (SMBC_parse_path(frame,
1764 context,
1765 fname,
1766 &workgroup,
1767 &server,
1768 &port,
1769 &share,
1770 &path,
1771 &user,
1772 &password,
1773 NULL)) {
1774 errno = EINVAL;
1775 TALLOC_FREE(frame);
1776 return -1;
1779 if (!user || user[0] == (char)0) {
1780 user = talloc_strdup(frame, smbc_getUser(context));
1781 if (!user) {
1782 errno = ENOMEM;
1783 TALLOC_FREE(frame);
1784 return -1;
1788 srv = SMBC_server(frame, context, True,
1789 server, port, share, &workgroup, &user, &password);
1790 if (!srv) {
1791 TALLOC_FREE(frame);
1792 return -1; /* errno set by SMBC_server */
1795 if (! srv->no_nt_session) {
1796 ipc_srv = SMBC_attr_server(frame, context, server, port, share,
1797 &workgroup, &user, &password);
1798 if (! ipc_srv) {
1799 srv->no_nt_session = True;
1801 } else {
1802 ipc_srv = NULL;
1806 * Are they asking to set the entire set of known attributes?
1808 if (strcasecmp_m(name, "system.*") == 0 ||
1809 strcasecmp_m(name, "system.*+") == 0) {
1810 /* Yup. */
1811 char *namevalue =
1812 talloc_asprintf(talloc_tos(), "%s:%s",
1813 name+7, (const char *) value);
1814 if (! namevalue) {
1815 errno = ENOMEM;
1816 ret = -1;
1817 TALLOC_FREE(frame);
1818 return -1;
1821 if (ipc_srv) {
1822 ret = cacl_set(context, talloc_tos(), srv->cli,
1823 ipc_srv->cli, &ipc_srv->pol, path,
1824 namevalue,
1825 (*namevalue == '*'
1826 ? SMBC_XATTR_MODE_SET
1827 : SMBC_XATTR_MODE_ADD),
1828 flags);
1829 } else {
1830 ret = 0;
1833 /* get a DOS Attribute Descriptor with current attributes */
1834 dad = dos_attr_query(context, talloc_tos(), path, srv);
1835 if (dad) {
1836 bool ok;
1838 /* Overwrite old with new, using what was provided */
1839 dos_attr_parse(context, dad, srv, namevalue);
1841 /* Set the new DOS attributes */
1842 ok = SMBC_setatr(
1843 context,
1844 srv,
1845 path,
1846 (struct timespec) {
1847 .tv_sec = dad->create_time },
1848 (struct timespec) {
1849 .tv_sec = dad->access_time },
1850 (struct timespec) {
1851 .tv_sec = dad->write_time },
1852 (struct timespec) {
1853 .tv_sec = dad->change_time },
1854 dad->mode);
1855 if (!ok) {
1856 /* cause failure if NT failed too */
1857 dad = NULL;
1861 /* we only fail if both NT and DOS sets failed */
1862 if (ret < 0 && ! dad) {
1863 ret = -1; /* in case dad was null */
1865 else {
1866 ret = 0;
1869 TALLOC_FREE(frame);
1870 return ret;
1874 * Are they asking to set an access control element or to set
1875 * the entire access control list?
1877 if (strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
1878 strcasecmp_m(name, "system.nt_sec_desc.*+") == 0 ||
1879 strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
1880 strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
1881 strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0) {
1883 /* Yup. */
1884 char *namevalue =
1885 talloc_asprintf(talloc_tos(), "%s:%s",
1886 name+19, (const char *) value);
1888 if (! ipc_srv) {
1889 ret = -1; /* errno set by SMBC_server() */
1891 else if (! namevalue) {
1892 errno = ENOMEM;
1893 ret = -1;
1894 } else {
1895 ret = cacl_set(context, talloc_tos(), srv->cli,
1896 ipc_srv->cli, &ipc_srv->pol, path,
1897 namevalue,
1898 (*namevalue == '*'
1899 ? SMBC_XATTR_MODE_SET
1900 : SMBC_XATTR_MODE_ADD),
1901 flags);
1903 TALLOC_FREE(frame);
1904 return ret;
1908 * Are they asking to set the owner?
1910 if (strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
1911 strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0) {
1913 /* Yup. */
1914 char *namevalue =
1915 talloc_asprintf(talloc_tos(), "%s:%s",
1916 name+19, (const char *) value);
1918 if (! ipc_srv) {
1919 ret = -1; /* errno set by SMBC_server() */
1921 else if (! namevalue) {
1922 errno = ENOMEM;
1923 ret = -1;
1924 } else {
1925 ret = cacl_set(context, talloc_tos(), srv->cli,
1926 ipc_srv->cli, &ipc_srv->pol, path,
1927 namevalue, SMBC_XATTR_MODE_CHOWN, 0);
1929 TALLOC_FREE(frame);
1930 return ret;
1934 * Are they asking to set the group?
1936 if (strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
1937 strcasecmp_m(name, "system.nt_sec_desc.group+") == 0) {
1939 /* Yup. */
1940 char *namevalue =
1941 talloc_asprintf(talloc_tos(), "%s:%s",
1942 name+19, (const char *) value);
1944 if (! ipc_srv) {
1945 /* errno set by SMBC_server() */
1946 ret = -1;
1948 else if (! namevalue) {
1949 errno = ENOMEM;
1950 ret = -1;
1951 } else {
1952 ret = cacl_set(context, talloc_tos(), srv->cli,
1953 ipc_srv->cli, &ipc_srv->pol, path,
1954 namevalue, SMBC_XATTR_MODE_CHGRP, 0);
1956 TALLOC_FREE(frame);
1957 return ret;
1960 /* Determine whether to use old-style or new-style attribute names */
1961 if (context->internal->full_time_names) {
1962 /* new-style names */
1963 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
1964 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
1965 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
1966 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
1967 } else {
1968 /* old-style names */
1969 attr_strings.create_time_attr = NULL;
1970 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
1971 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
1972 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
1976 * Are they asking to set a DOS attribute?
1978 if (strcasecmp_m(name, "system.dos_attr.*") == 0 ||
1979 strcasecmp_m(name, "system.dos_attr.mode") == 0 ||
1980 (attr_strings.create_time_attr != NULL &&
1981 strcasecmp_m(name, attr_strings.create_time_attr) == 0) ||
1982 strcasecmp_m(name, attr_strings.access_time_attr) == 0 ||
1983 strcasecmp_m(name, attr_strings.write_time_attr) == 0 ||
1984 strcasecmp_m(name, attr_strings.change_time_attr) == 0) {
1986 /* get a DOS Attribute Descriptor with current attributes */
1987 dad = dos_attr_query(context, talloc_tos(), path, srv);
1988 if (dad) {
1989 char *namevalue =
1990 talloc_asprintf(talloc_tos(), "%s:%s",
1991 name+16, (const char *) value);
1992 if (! namevalue) {
1993 errno = ENOMEM;
1994 ret = -1;
1995 } else {
1996 /* Overwrite old with provided new params */
1997 dos_attr_parse(context, dad, srv, namevalue);
1999 /* Set the new DOS attributes */
2000 ret2 = SMBC_setatr(
2001 context,
2002 srv,
2003 path,
2004 (struct timespec) {
2005 .tv_sec = dad->create_time },
2006 (struct timespec) {
2007 .tv_sec = dad->access_time },
2008 (struct timespec) {
2009 .tv_sec = dad->write_time },
2010 (struct timespec) {
2011 .tv_sec = dad->change_time },
2012 dad->mode);
2014 /* ret2 has True (success) / False (failure) */
2015 if (ret2) {
2016 ret = 0;
2017 } else {
2018 ret = -1;
2021 } else {
2022 ret = -1;
2025 TALLOC_FREE(frame);
2026 return ret;
2029 /* Unsupported attribute name */
2030 errno = EINVAL;
2031 TALLOC_FREE(frame);
2032 return -1;
2036 SMBC_getxattr_ctx(SMBCCTX *context,
2037 const char *fname,
2038 const char *name,
2039 const void *value,
2040 size_t size)
2042 int ret;
2043 SMBCSRV *srv = NULL;
2044 SMBCSRV *ipc_srv = NULL;
2045 char *server = NULL;
2046 char *share = NULL;
2047 char *user = NULL;
2048 char *password = NULL;
2049 char *workgroup = NULL;
2050 char *path = NULL;
2051 struct {
2052 const char * create_time_attr;
2053 const char * access_time_attr;
2054 const char * write_time_attr;
2055 const char * change_time_attr;
2056 } attr_strings;
2057 uint16_t port = 0;
2058 TALLOC_CTX *frame = talloc_stackframe();
2060 if (!context || !context->internal->initialized) {
2061 errno = EINVAL; /* Best I can think of ... */
2062 TALLOC_FREE(frame);
2063 return -1;
2066 if (!fname) {
2067 errno = EINVAL;
2068 TALLOC_FREE(frame);
2069 return -1;
2072 DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
2074 if (SMBC_parse_path(frame,
2075 context,
2076 fname,
2077 &workgroup,
2078 &server,
2079 &port,
2080 &share,
2081 &path,
2082 &user,
2083 &password,
2084 NULL)) {
2085 errno = EINVAL;
2086 TALLOC_FREE(frame);
2087 return -1;
2090 if (!user || user[0] == '\0') {
2091 user = talloc_strdup(frame, smbc_getUser(context));
2092 if (!user) {
2093 errno = ENOMEM;
2094 TALLOC_FREE(frame);
2095 return -1;
2099 srv = SMBC_server(frame, context, True,
2100 server, port, share, &workgroup, &user, &password);
2101 if (!srv) {
2102 TALLOC_FREE(frame);
2103 return -1; /* errno set by SMBC_server */
2106 if (! srv->no_nt_session) {
2107 ipc_srv = SMBC_attr_server(frame, context, server, port, share,
2108 &workgroup, &user, &password);
2110 * SMBC_attr_server() can cause the original
2111 * server to be removed from the cache.
2112 * If so we must error out here as the srv
2113 * pointer has been freed.
2115 if (smbc_getFunctionGetCachedServer(context)(context,
2116 server,
2117 share,
2118 workgroup,
2119 user) != srv) {
2120 #if defined(ECONNRESET)
2121 errno = ECONNRESET;
2122 #else
2123 errno = ETIMEDOUT;
2124 #endif
2125 TALLOC_FREE(frame);
2126 return -1;
2128 if (! ipc_srv) {
2129 srv->no_nt_session = True;
2131 } else {
2132 ipc_srv = NULL;
2135 /* Determine whether to use old-style or new-style attribute names */
2136 if (context->internal->full_time_names) {
2137 /* new-style names */
2138 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
2139 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
2140 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
2141 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
2142 } else {
2143 /* old-style names */
2144 attr_strings.create_time_attr = NULL;
2145 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
2146 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
2147 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
2150 /* Are they requesting a supported attribute? */
2151 if (strcasecmp_m(name, "system.*") == 0 ||
2152 strncasecmp_m(name, "system.*!", 9) == 0 ||
2153 strcasecmp_m(name, "system.*+") == 0 ||
2154 strncasecmp_m(name, "system.*+!", 10) == 0 ||
2155 strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
2156 strncasecmp_m(name, "system.nt_sec_desc.*!", 21) == 0 ||
2157 strcasecmp_m(name, "system.nt_sec_desc.*+") == 0 ||
2158 strncasecmp_m(name, "system.nt_sec_desc.*+!", 22) == 0 ||
2159 strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
2160 strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
2161 strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0 ||
2162 strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
2163 strcasecmp_m(name, "system.nt_sec_desc.group+") == 0 ||
2164 strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
2165 strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0 ||
2166 strcasecmp_m(name, "system.dos_attr.*") == 0 ||
2167 strncasecmp_m(name, "system.dos_attr.*!", 18) == 0 ||
2168 strcasecmp_m(name, "system.dos_attr.mode") == 0 ||
2169 strcasecmp_m(name, "system.dos_attr.size") == 0 ||
2170 (attr_strings.create_time_attr != NULL &&
2171 strcasecmp_m(name, attr_strings.create_time_attr) == 0) ||
2172 strcasecmp_m(name, attr_strings.access_time_attr) == 0 ||
2173 strcasecmp_m(name, attr_strings.write_time_attr) == 0 ||
2174 strcasecmp_m(name, attr_strings.change_time_attr) == 0 ||
2175 strcasecmp_m(name, "system.dos_attr.inode") == 0) {
2177 /* Yup. */
2178 const char *filename = name;
2179 ret = cacl_get(context, talloc_tos(), srv,
2180 ipc_srv == NULL ? NULL : ipc_srv->cli,
2181 &ipc_srv->pol, path,
2182 filename,
2183 discard_const_p(char, value),
2184 size);
2185 TALLOC_FREE(frame);
2187 * static function cacl_get returns a value greater than zero
2188 * which is needed buffer size needed when size_t is 0.
2190 return ret;
2193 /* Unsupported attribute name */
2194 errno = EINVAL;
2195 TALLOC_FREE(frame);
2196 return -1;
2201 SMBC_removexattr_ctx(SMBCCTX *context,
2202 const char *fname,
2203 const char *name)
2205 int ret;
2206 SMBCSRV *srv = NULL;
2207 SMBCSRV *ipc_srv = NULL;
2208 char *server = NULL;
2209 char *share = NULL;
2210 char *user = NULL;
2211 char *password = NULL;
2212 char *workgroup = NULL;
2213 char *path = NULL;
2214 uint16_t port = 0;
2215 TALLOC_CTX *frame = talloc_stackframe();
2217 if (!context || !context->internal->initialized) {
2218 errno = EINVAL; /* Best I can think of ... */
2219 TALLOC_FREE(frame);
2220 return -1;
2223 if (!fname) {
2224 errno = EINVAL;
2225 TALLOC_FREE(frame);
2226 return -1;
2229 DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
2231 if (SMBC_parse_path(frame,
2232 context,
2233 fname,
2234 &workgroup,
2235 &server,
2236 &port,
2237 &share,
2238 &path,
2239 &user,
2240 &password,
2241 NULL)) {
2242 errno = EINVAL;
2243 TALLOC_FREE(frame);
2244 return -1;
2247 if (!user || user[0] == (char)0) {
2248 user = talloc_strdup(frame, smbc_getUser(context));
2249 if (!user) {
2250 errno = ENOMEM;
2251 TALLOC_FREE(frame);
2252 return -1;
2256 srv = SMBC_server(frame, context, True,
2257 server, port, share, &workgroup, &user, &password);
2258 if (!srv) {
2259 TALLOC_FREE(frame);
2260 return -1; /* errno set by SMBC_server */
2263 if (! srv->no_nt_session) {
2264 int saved_errno;
2265 ipc_srv = SMBC_attr_server(frame, context, server, port, share,
2266 &workgroup, &user, &password);
2267 saved_errno = errno;
2269 * SMBC_attr_server() can cause the original
2270 * server to be removed from the cache.
2271 * If so we must error out here as the srv
2272 * pointer has been freed.
2274 if (smbc_getFunctionGetCachedServer(context)(context,
2275 server,
2276 share,
2277 workgroup,
2278 user) != srv) {
2279 #if defined(ECONNRESET)
2280 errno = ECONNRESET;
2281 #else
2282 errno = ETIMEDOUT;
2283 #endif
2284 TALLOC_FREE(frame);
2285 return -1;
2287 if (! ipc_srv) {
2288 errno = saved_errno;
2289 srv->no_nt_session = True;
2291 } else {
2292 ipc_srv = NULL;
2295 if (! ipc_srv) {
2296 TALLOC_FREE(frame);
2297 return -1; /* errno set by SMBC_attr_server */
2300 /* Are they asking to set the entire ACL? */
2301 if (strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
2302 strcasecmp_m(name, "system.nt_sec_desc.*+") == 0) {
2304 /* Yup. */
2305 ret = cacl_set(context, talloc_tos(), srv->cli,
2306 ipc_srv->cli, &ipc_srv->pol, path,
2307 NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
2308 TALLOC_FREE(frame);
2309 return ret;
2313 * Are they asking to remove one or more specific security descriptor
2314 * attributes?
2316 if (strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
2317 strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
2318 strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0 ||
2319 strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
2320 strcasecmp_m(name, "system.nt_sec_desc.group+") == 0 ||
2321 strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
2322 strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0) {
2324 /* Yup. */
2325 ret = cacl_set(context, talloc_tos(), srv->cli,
2326 ipc_srv->cli, &ipc_srv->pol, path,
2327 discard_const_p(char, name) + 19,
2328 SMBC_XATTR_MODE_REMOVE, 0);
2329 TALLOC_FREE(frame);
2330 return ret;
2333 /* Unsupported attribute name */
2334 errno = EINVAL;
2335 TALLOC_FREE(frame);
2336 return -1;
2340 SMBC_listxattr_ctx(SMBCCTX *context,
2341 const char *fname,
2342 char *list,
2343 size_t size)
2346 * This isn't quite what listxattr() is supposed to do. This returns
2347 * the complete set of attribute names, always, rather than only those
2348 * attribute names which actually exist for a file. Hmmm...
2350 size_t retsize;
2351 static const char supported_old[] =
2352 "system.*\0"
2353 "system.*+\0"
2354 "system.nt_sec_desc.revision\0"
2355 "system.nt_sec_desc.owner\0"
2356 "system.nt_sec_desc.owner+\0"
2357 "system.nt_sec_desc.group\0"
2358 "system.nt_sec_desc.group+\0"
2359 "system.nt_sec_desc.acl.*\0"
2360 "system.nt_sec_desc.acl\0"
2361 "system.nt_sec_desc.acl+\0"
2362 "system.nt_sec_desc.*\0"
2363 "system.nt_sec_desc.*+\0"
2364 "system.dos_attr.*\0"
2365 "system.dos_attr.mode\0"
2366 "system.dos_attr.c_time\0"
2367 "system.dos_attr.a_time\0"
2368 "system.dos_attr.m_time\0"
2370 static const char supported_new[] =
2371 "system.*\0"
2372 "system.*+\0"
2373 "system.nt_sec_desc.revision\0"
2374 "system.nt_sec_desc.owner\0"
2375 "system.nt_sec_desc.owner+\0"
2376 "system.nt_sec_desc.group\0"
2377 "system.nt_sec_desc.group+\0"
2378 "system.nt_sec_desc.acl.*\0"
2379 "system.nt_sec_desc.acl\0"
2380 "system.nt_sec_desc.acl+\0"
2381 "system.nt_sec_desc.*\0"
2382 "system.nt_sec_desc.*+\0"
2383 "system.dos_attr.*\0"
2384 "system.dos_attr.mode\0"
2385 "system.dos_attr.create_time\0"
2386 "system.dos_attr.access_time\0"
2387 "system.dos_attr.write_time\0"
2388 "system.dos_attr.change_time\0"
2390 const char * supported;
2392 if (context->internal->full_time_names) {
2393 supported = supported_new;
2394 retsize = sizeof(supported_new);
2395 } else {
2396 supported = supported_old;
2397 retsize = sizeof(supported_old);
2400 if (size == 0) {
2401 return retsize;
2404 if (retsize > size) {
2405 errno = ERANGE;
2406 return -1;
2409 /* this can't be strcpy() because there are embedded null characters */
2410 memcpy(list, supported, retsize);
2411 return retsize;