ctdb-daemon: Allow flag TDB_MUTEX_LOCKING to pass into db_attach
[Samba.git] / source3 / libsmb / libsmb_xattr.c
blob84937766fe2ba1f371e6e7a5322fbaf3ca5e84b3
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"
35 * Find an lsa pipe handle associated with a cli struct.
37 static struct rpc_pipe_client *
38 find_lsa_pipe_hnd(struct cli_state *ipc_cli)
40 struct rpc_pipe_client *pipe_hnd;
42 for (pipe_hnd = ipc_cli->pipe_list;
43 pipe_hnd;
44 pipe_hnd = pipe_hnd->next) {
45 if (ndr_syntax_id_equal(&pipe_hnd->abstract_syntax,
46 &ndr_table_lsarpc.syntax_id)) {
47 return pipe_hnd;
50 return NULL;
54 * Sort ACEs according to the documentation at
55 * http://support.microsoft.com/kb/269175, at least as far as it defines the
56 * order.
59 static int
60 ace_compare(struct security_ace *ace1,
61 struct security_ace *ace2)
63 bool b1;
64 bool b2;
66 /* If the ACEs are equal, we have nothing more to do. */
67 if (security_ace_equal(ace1, ace2)) {
68 return 0;
71 /* Inherited follow non-inherited */
72 b1 = ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
73 b2 = ((ace2->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
74 if (b1 != b2) {
75 return (b1 ? 1 : -1);
79 * What shall we do with AUDITs and ALARMs? It's undefined. We'll
80 * sort them after DENY and ALLOW.
82 b1 = (ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
83 ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
84 ace1->type != SEC_ACE_TYPE_ACCESS_DENIED &&
85 ace1->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
86 b2 = (ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
87 ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
88 ace2->type != SEC_ACE_TYPE_ACCESS_DENIED &&
89 ace2->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
90 if (b1 != b2) {
91 return (b1 ? 1 : -1);
94 /* Allowed ACEs follow denied ACEs */
95 b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
96 ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
97 b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
98 ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
99 if (b1 != b2) {
100 return (b1 ? 1 : -1);
104 * ACEs applying to an entity's object follow those applying to the
105 * entity itself
107 b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
108 ace1->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
109 b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
110 ace2->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
111 if (b1 != b2) {
112 return (b1 ? 1 : -1);
116 * If we get this far, the ACEs are similar as far as the
117 * characteristics we typically care about (those defined by the
118 * referenced MS document). We'll now sort by characteristics that
119 * just seems reasonable.
122 if (ace1->type != ace2->type) {
123 return ace2->type - ace1->type;
126 if (dom_sid_compare(&ace1->trustee, &ace2->trustee)) {
127 return dom_sid_compare(&ace1->trustee, &ace2->trustee);
130 if (ace1->flags != ace2->flags) {
131 return ace1->flags - ace2->flags;
134 if (ace1->access_mask != ace2->access_mask) {
135 return ace1->access_mask - ace2->access_mask;
138 if (ace1->size != ace2->size) {
139 return ace1->size - ace2->size;
142 return memcmp(ace1, ace2, sizeof(struct security_ace));
146 static void
147 sort_acl(struct security_acl *the_acl)
149 uint32 i;
150 if (!the_acl) return;
152 TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
154 for (i=1;i<the_acl->num_aces;) {
155 if (security_ace_equal(&the_acl->aces[i-1],
156 &the_acl->aces[i])) {
157 int j;
158 for (j=i; j<the_acl->num_aces-1; j++) {
159 the_acl->aces[j] = the_acl->aces[j+1];
161 the_acl->num_aces--;
162 } else {
163 i++;
168 /* convert a SID to a string, either numeric or username/group */
169 static void
170 convert_sid_to_string(struct cli_state *ipc_cli,
171 struct policy_handle *pol,
172 fstring str,
173 bool numeric,
174 struct dom_sid *sid)
176 char **domains = NULL;
177 char **names = NULL;
178 enum lsa_SidType *types = NULL;
179 struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
180 TALLOC_CTX *ctx;
182 sid_to_fstring(str, sid);
184 if (numeric) {
185 return; /* no lookup desired */
188 if (!pipe_hnd) {
189 return;
192 /* Ask LSA to convert the sid to a name */
194 ctx = talloc_stackframe();
196 if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ctx,
197 pol, 1, sid, &domains,
198 &names, &types)) ||
199 !domains || !domains[0] || !names || !names[0]) {
200 TALLOC_FREE(ctx);
201 return;
204 /* Converted OK */
206 slprintf(str, sizeof(fstring) - 1, "%s%s%s",
207 domains[0], lp_winbind_separator(),
208 names[0]);
210 TALLOC_FREE(ctx);
213 /* convert a string to a SID, either numeric or username/group */
214 static bool
215 convert_string_to_sid(struct cli_state *ipc_cli,
216 struct policy_handle *pol,
217 bool numeric,
218 struct dom_sid *sid,
219 const char *str)
221 enum lsa_SidType *types = NULL;
222 struct dom_sid *sids = NULL;
223 bool result = True;
224 TALLOC_CTX *ctx = NULL;
225 struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
227 if (!pipe_hnd) {
228 return False;
231 if (numeric) {
232 if (strncmp(str, "S-", 2) == 0) {
233 return string_to_sid(sid, str);
236 result = False;
237 goto done;
240 ctx = talloc_stackframe();
241 if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ctx,
242 pol, 1, &str,
243 NULL, 1, &sids,
244 &types))) {
245 result = False;
246 goto done;
249 sid_copy(sid, &sids[0]);
250 done:
251 TALLOC_FREE(ctx);
252 return result;
256 /* parse an struct security_ace in the same format as print_ace() */
257 static bool
258 parse_ace(struct cli_state *ipc_cli,
259 struct policy_handle *pol,
260 struct security_ace *ace,
261 bool numeric,
262 char *str)
264 char *p;
265 const char *cp;
266 char *tok;
267 unsigned int atype;
268 unsigned int aflags;
269 unsigned int amask;
270 struct dom_sid sid;
271 uint32_t mask;
272 const struct perm_value *v;
273 struct perm_value {
274 const char perm[7];
275 uint32 mask;
277 TALLOC_CTX *frame = talloc_stackframe();
279 /* These values discovered by inspection */
280 static const struct perm_value special_values[] = {
281 { "R", 0x00120089 },
282 { "W", 0x00120116 },
283 { "X", 0x001200a0 },
284 { "D", 0x00010000 },
285 { "P", 0x00040000 },
286 { "O", 0x00080000 },
287 { "", 0 },
290 static const struct perm_value standard_values[] = {
291 { "READ", 0x001200a9 },
292 { "CHANGE", 0x001301bf },
293 { "FULL", 0x001f01ff },
294 { "", 0 },
297 ZERO_STRUCTP(ace);
298 p = strchr_m(str,':');
299 if (!p) {
300 TALLOC_FREE(frame);
301 return False;
303 *p = '\0';
304 p++;
305 /* Try to parse numeric form */
307 if (sscanf(p, "%u/%u/%u", &atype, &aflags, &amask) == 3 &&
308 convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
309 goto done;
312 /* Try to parse text form */
314 if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
315 TALLOC_FREE(frame);
316 return false;
319 cp = p;
320 if (!next_token_talloc(frame, &cp, &tok, "/")) {
321 TALLOC_FREE(frame);
322 return false;
325 if (strncasecmp_m(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
326 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
327 } else if (strncasecmp_m(tok, "DENIED", strlen("DENIED")) == 0) {
328 atype = SEC_ACE_TYPE_ACCESS_DENIED;
329 } else {
330 TALLOC_FREE(frame);
331 return false;
334 /* Only numeric form accepted for flags at present */
336 if (!(next_token_talloc(frame, &cp, &tok, "/") &&
337 sscanf(tok, "%u", &aflags))) {
338 TALLOC_FREE(frame);
339 return false;
342 if (!next_token_talloc(frame, &cp, &tok, "/")) {
343 TALLOC_FREE(frame);
344 return false;
347 if (strncmp(tok, "0x", 2) == 0) {
348 if (sscanf(tok, "%u", &amask) != 1) {
349 TALLOC_FREE(frame);
350 return false;
352 goto done;
355 for (v = standard_values; v != NULL; v++) {
356 if (strcmp(tok, v->perm) == 0) {
357 amask = v->mask;
358 goto done;
362 p = tok;
364 while(*p) {
365 bool found = False;
367 for (v = special_values; v != NULL; v++) {
368 if (v->perm[0] == *p) {
369 amask |= v->mask;
370 found = True;
374 if (!found) {
375 TALLOC_FREE(frame);
376 return false;
378 p++;
381 if (*p) {
382 TALLOC_FREE(frame);
383 return false;
386 done:
387 mask = amask;
388 init_sec_ace(ace, &sid, atype, mask, aflags);
389 TALLOC_FREE(frame);
390 return true;
393 /* add an struct security_ace to a list of struct security_aces in a struct security_acl */
394 static bool
395 add_ace(struct security_acl **the_acl,
396 struct security_ace *ace,
397 TALLOC_CTX *ctx)
399 struct security_acl *newacl;
400 struct security_ace *aces;
402 if (! *the_acl) {
403 (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
404 return True;
407 if ((aces = SMB_CALLOC_ARRAY(struct security_ace,
408 1+(*the_acl)->num_aces)) == NULL) {
409 return False;
411 memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(struct security_ace));
412 memcpy(aces+(*the_acl)->num_aces, ace, sizeof(struct security_ace));
413 newacl = make_sec_acl(ctx, (*the_acl)->revision,
414 1+(*the_acl)->num_aces, aces);
415 SAFE_FREE(aces);
416 (*the_acl) = newacl;
417 return True;
421 /* parse a ascii version of a security descriptor */
422 static struct security_descriptor *
423 sec_desc_parse(TALLOC_CTX *ctx,
424 struct cli_state *ipc_cli,
425 struct policy_handle *pol,
426 bool numeric,
427 const char *str)
429 const char *p = str;
430 char *tok;
431 struct security_descriptor *ret = NULL;
432 size_t sd_size;
433 struct dom_sid *group_sid=NULL;
434 struct dom_sid *owner_sid=NULL;
435 struct security_acl *dacl=NULL;
436 int revision=1;
438 while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
440 if (strncasecmp_m(tok,"REVISION:", 9) == 0) {
441 revision = strtol(tok+9, NULL, 16);
442 continue;
445 if (strncasecmp_m(tok,"OWNER:", 6) == 0) {
446 if (owner_sid) {
447 DEBUG(5,("OWNER specified more than once!\n"));
448 goto done;
450 owner_sid = SMB_CALLOC_ARRAY(struct dom_sid, 1);
451 if (!owner_sid ||
452 !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 continue;
461 if (strncasecmp_m(tok,"OWNER+:", 7) == 0) {
462 if (owner_sid) {
463 DEBUG(5,("OWNER specified more than once!\n"));
464 goto done;
466 owner_sid = SMB_CALLOC_ARRAY(struct dom_sid, 1);
467 if (!owner_sid ||
468 !convert_string_to_sid(ipc_cli, pol,
469 False,
470 owner_sid, tok+7)) {
471 DEBUG(5, ("Failed to parse owner sid\n"));
472 goto done;
474 continue;
477 if (strncasecmp_m(tok,"GROUP:", 6) == 0) {
478 if (group_sid) {
479 DEBUG(5,("GROUP specified more than once!\n"));
480 goto done;
482 group_sid = SMB_CALLOC_ARRAY(struct dom_sid, 1);
483 if (!group_sid ||
484 !convert_string_to_sid(ipc_cli, pol,
485 numeric,
486 group_sid, tok+6)) {
487 DEBUG(5, ("Failed to parse group sid\n"));
488 goto done;
490 continue;
493 if (strncasecmp_m(tok,"GROUP+:", 7) == 0) {
494 if (group_sid) {
495 DEBUG(5,("GROUP specified more than once!\n"));
496 goto done;
498 group_sid = SMB_CALLOC_ARRAY(struct dom_sid, 1);
499 if (!group_sid ||
500 !convert_string_to_sid(ipc_cli, pol,
501 False,
502 group_sid, tok+6)) {
503 DEBUG(5, ("Failed to parse group sid\n"));
504 goto done;
506 continue;
509 if (strncasecmp_m(tok,"ACL:", 4) == 0) {
510 struct security_ace ace;
511 if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
512 DEBUG(5, ("Failed to parse ACL %s\n", tok));
513 goto done;
515 if(!add_ace(&dacl, &ace, ctx)) {
516 DEBUG(5, ("Failed to add ACL %s\n", tok));
517 goto done;
519 continue;
522 if (strncasecmp_m(tok,"ACL+:", 5) == 0) {
523 struct security_ace ace;
524 if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
525 DEBUG(5, ("Failed to parse ACL %s\n", tok));
526 goto done;
528 if(!add_ace(&dacl, &ace, ctx)) {
529 DEBUG(5, ("Failed to add ACL %s\n", tok));
530 goto done;
532 continue;
535 DEBUG(5, ("Failed to parse security descriptor\n"));
536 goto done;
539 ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE,
540 owner_sid, group_sid, NULL, dacl, &sd_size);
542 done:
543 SAFE_FREE(group_sid);
544 SAFE_FREE(owner_sid);
545 return ret;
549 /* Obtain the current dos attributes */
550 static DOS_ATTR_DESC *
551 dos_attr_query(SMBCCTX *context,
552 TALLOC_CTX *ctx,
553 const char *filename,
554 SMBCSRV *srv)
556 struct timespec create_time_ts;
557 struct timespec write_time_ts;
558 struct timespec access_time_ts;
559 struct timespec change_time_ts;
560 off_t size = 0;
561 uint16 mode = 0;
562 SMB_INO_T inode = 0;
563 DOS_ATTR_DESC *ret;
565 ret = talloc(ctx, DOS_ATTR_DESC);
566 if (!ret) {
567 errno = ENOMEM;
568 return NULL;
571 /* Obtain the DOS attributes */
572 if (!SMBC_getatr(context, srv, filename,
573 &mode, &size,
574 &create_time_ts,
575 &access_time_ts,
576 &write_time_ts,
577 &change_time_ts,
578 &inode)) {
579 errno = SMBC_errno(context, srv->cli);
580 DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
581 TALLOC_FREE(ret);
582 return NULL;
585 ret->mode = mode;
586 ret->size = size;
587 ret->create_time = convert_timespec_to_time_t(create_time_ts);
588 ret->access_time = convert_timespec_to_time_t(access_time_ts);
589 ret->write_time = convert_timespec_to_time_t(write_time_ts);
590 ret->change_time = convert_timespec_to_time_t(change_time_ts);
591 ret->inode = inode;
593 return ret;
597 /* parse a ascii version of a security descriptor */
598 static void
599 dos_attr_parse(SMBCCTX *context,
600 DOS_ATTR_DESC *dad,
601 SMBCSRV *srv,
602 char *str)
604 int n;
605 const char *p = str;
606 char *tok = NULL;
607 TALLOC_CTX *frame = NULL;
608 struct {
609 const char * create_time_attr;
610 const char * access_time_attr;
611 const char * write_time_attr;
612 const char * change_time_attr;
613 } attr_strings;
615 /* Determine whether to use old-style or new-style attribute names */
616 if (context->internal->full_time_names) {
617 /* new-style names */
618 attr_strings.create_time_attr = "CREATE_TIME";
619 attr_strings.access_time_attr = "ACCESS_TIME";
620 attr_strings.write_time_attr = "WRITE_TIME";
621 attr_strings.change_time_attr = "CHANGE_TIME";
622 } else {
623 /* old-style names */
624 attr_strings.create_time_attr = NULL;
625 attr_strings.access_time_attr = "A_TIME";
626 attr_strings.write_time_attr = "M_TIME";
627 attr_strings.change_time_attr = "C_TIME";
630 /* if this is to set the entire ACL... */
631 if (*str == '*') {
632 /* ... then increment past the first colon if there is one */
633 if ((p = strchr(str, ':')) != NULL) {
634 ++p;
635 } else {
636 p = str;
640 frame = talloc_stackframe();
641 while (next_token_talloc(frame, &p, &tok, "\t,\r\n")) {
642 if (strncasecmp_m(tok, "MODE:", 5) == 0) {
643 long request = strtol(tok+5, NULL, 16);
644 if (request == 0) {
645 dad->mode = (request |
646 (IS_DOS_DIR(dad->mode)
647 ? FILE_ATTRIBUTE_DIRECTORY
648 : FILE_ATTRIBUTE_NORMAL));
649 } else {
650 dad->mode = request;
652 continue;
655 if (strncasecmp_m(tok, "SIZE:", 5) == 0) {
656 dad->size = (off_t)atof(tok+5);
657 continue;
660 n = strlen(attr_strings.access_time_attr);
661 if (strncasecmp_m(tok, attr_strings.access_time_attr, n) == 0) {
662 dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
663 continue;
666 n = strlen(attr_strings.change_time_attr);
667 if (strncasecmp_m(tok, attr_strings.change_time_attr, n) == 0) {
668 dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
669 continue;
672 n = strlen(attr_strings.write_time_attr);
673 if (strncasecmp_m(tok, attr_strings.write_time_attr, n) == 0) {
674 dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
675 continue;
678 if (attr_strings.create_time_attr != NULL) {
679 n = strlen(attr_strings.create_time_attr);
680 if (strncasecmp_m(tok, attr_strings.create_time_attr,
681 n) == 0) {
682 dad->create_time = (time_t)strtol(tok+n+1,
683 NULL, 10);
684 continue;
688 if (strncasecmp_m(tok, "INODE:", 6) == 0) {
689 dad->inode = (SMB_INO_T)atof(tok+6);
690 continue;
693 TALLOC_FREE(frame);
696 /*****************************************************
697 Retrieve the acls for a file.
698 *******************************************************/
700 static int
701 cacl_get(SMBCCTX *context,
702 TALLOC_CTX *ctx,
703 SMBCSRV *srv,
704 struct cli_state *ipc_cli,
705 struct policy_handle *pol,
706 const char *filename,
707 const char *attr_name,
708 char *buf,
709 int bufsize)
711 uint32 i;
712 int n = 0;
713 int n_used;
714 bool all;
715 bool all_nt;
716 bool all_nt_acls;
717 bool all_dos;
718 bool some_nt;
719 bool some_dos;
720 bool exclude_nt_revision = False;
721 bool exclude_nt_owner = False;
722 bool exclude_nt_group = False;
723 bool exclude_nt_acl = False;
724 bool exclude_dos_mode = False;
725 bool exclude_dos_size = False;
726 bool exclude_dos_create_time = False;
727 bool exclude_dos_access_time = False;
728 bool exclude_dos_write_time = False;
729 bool exclude_dos_change_time = False;
730 bool exclude_dos_inode = False;
731 bool numeric = True;
732 bool determine_size = (bufsize == 0);
733 uint16_t fnum;
734 struct security_descriptor *sd;
735 fstring sidstr;
736 fstring name_sandbox;
737 char *name;
738 char *pExclude;
739 char *p;
740 struct timespec create_time_ts;
741 struct timespec write_time_ts;
742 struct timespec access_time_ts;
743 struct timespec change_time_ts;
744 time_t create_time = (time_t)0;
745 time_t write_time = (time_t)0;
746 time_t access_time = (time_t)0;
747 time_t change_time = (time_t)0;
748 off_t size = 0;
749 uint16 mode = 0;
750 SMB_INO_T ino = 0;
751 struct cli_state *cli = srv->cli;
752 struct {
753 const char * create_time_attr;
754 const char * access_time_attr;
755 const char * write_time_attr;
756 const char * change_time_attr;
757 } attr_strings;
758 struct {
759 const char * create_time_attr;
760 const char * access_time_attr;
761 const char * write_time_attr;
762 const char * change_time_attr;
763 } excl_attr_strings;
765 /* Determine whether to use old-style or new-style attribute names */
766 if (context->internal->full_time_names) {
767 /* new-style names */
768 attr_strings.create_time_attr = "CREATE_TIME";
769 attr_strings.access_time_attr = "ACCESS_TIME";
770 attr_strings.write_time_attr = "WRITE_TIME";
771 attr_strings.change_time_attr = "CHANGE_TIME";
773 excl_attr_strings.create_time_attr = "CREATE_TIME";
774 excl_attr_strings.access_time_attr = "ACCESS_TIME";
775 excl_attr_strings.write_time_attr = "WRITE_TIME";
776 excl_attr_strings.change_time_attr = "CHANGE_TIME";
777 } else {
778 /* old-style names */
779 attr_strings.create_time_attr = NULL;
780 attr_strings.access_time_attr = "A_TIME";
781 attr_strings.write_time_attr = "M_TIME";
782 attr_strings.change_time_attr = "C_TIME";
784 excl_attr_strings.create_time_attr = NULL;
785 excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
786 excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
787 excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
790 /* Copy name so we can strip off exclusions (if any are specified) */
791 strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1);
793 /* Ensure name is null terminated */
794 name_sandbox[sizeof(name_sandbox) - 1] = '\0';
796 /* Play in the sandbox */
797 name = name_sandbox;
799 /* If there are any exclusions, point to them and mask them from name */
800 if ((pExclude = strchr(name, '!')) != NULL)
802 *pExclude++ = '\0';
805 all = (strncasecmp_m(name, "system.*", 8) == 0);
806 all_nt = (strncasecmp_m(name, "system.nt_sec_desc.*", 20) == 0);
807 all_nt_acls = (strncasecmp_m(name, "system.nt_sec_desc.acl.*", 24) == 0);
808 all_dos = (strncasecmp_m(name, "system.dos_attr.*", 17) == 0);
809 some_nt = (strncasecmp_m(name, "system.nt_sec_desc.", 19) == 0);
810 some_dos = (strncasecmp_m(name, "system.dos_attr.", 16) == 0);
811 numeric = (* (name + strlen(name) - 1) != '+');
813 /* Look for exclusions from "all" requests */
814 if (all || all_nt || all_dos) {
815 /* Exclusions are delimited by '!' */
816 for (;
817 pExclude != NULL;
818 pExclude = (p == NULL ? NULL : p + 1)) {
820 /* Find end of this exclusion name */
821 if ((p = strchr(pExclude, '!')) != NULL)
823 *p = '\0';
826 /* Which exclusion name is this? */
827 if (strcasecmp_m(pExclude,
828 "nt_sec_desc.revision") == 0) {
829 exclude_nt_revision = True;
831 else if (strcasecmp_m(pExclude,
832 "nt_sec_desc.owner") == 0) {
833 exclude_nt_owner = True;
835 else if (strcasecmp_m(pExclude,
836 "nt_sec_desc.group") == 0) {
837 exclude_nt_group = True;
839 else if (strcasecmp_m(pExclude,
840 "nt_sec_desc.acl") == 0) {
841 exclude_nt_acl = True;
843 else if (strcasecmp_m(pExclude,
844 "dos_attr.mode") == 0) {
845 exclude_dos_mode = True;
847 else if (strcasecmp_m(pExclude,
848 "dos_attr.size") == 0) {
849 exclude_dos_size = True;
851 else if (excl_attr_strings.create_time_attr != NULL &&
852 strcasecmp_m(pExclude,
853 excl_attr_strings.change_time_attr) == 0) {
854 exclude_dos_create_time = True;
856 else if (strcasecmp_m(pExclude,
857 excl_attr_strings.access_time_attr) == 0) {
858 exclude_dos_access_time = True;
860 else if (strcasecmp_m(pExclude,
861 excl_attr_strings.write_time_attr) == 0) {
862 exclude_dos_write_time = True;
864 else if (strcasecmp_m(pExclude,
865 excl_attr_strings.change_time_attr) == 0) {
866 exclude_dos_change_time = True;
868 else if (strcasecmp_m(pExclude, "dos_attr.inode") == 0) {
869 exclude_dos_inode = True;
871 else {
872 DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
873 pExclude));
874 errno = ENOATTR;
875 return -1;
880 n_used = 0;
883 * If we are (possibly) talking to an NT or new system and some NT
884 * attributes have been requested...
886 if (ipc_cli && (all || some_nt || all_nt_acls)) {
887 char *targetpath = NULL;
888 struct cli_state *targetcli = NULL;
889 NTSTATUS status;
891 /* Point to the portion after "system.nt_sec_desc." */
892 name += 19; /* if (all) this will be invalid but unused */
894 status = cli_resolve_path(
895 ctx, "", context->internal->auth_info,
896 cli, filename, &targetcli, &targetpath);
897 if (!NT_STATUS_IS_OK(status)) {
898 DEBUG(5, ("cacl_get Could not resolve %s\n",
899 filename));
900 errno = ENOENT;
901 return -1;
904 /* ... then obtain any NT attributes which were requested */
905 status = cli_ntcreate(targetcli, targetpath, 0,
906 CREATE_ACCESS_READ, 0,
907 FILE_SHARE_READ|FILE_SHARE_WRITE,
908 FILE_OPEN, 0x0, 0x0, &fnum, NULL);
909 if (!NT_STATUS_IS_OK(status)) {
910 DEBUG(5, ("cacl_get failed to open %s: %s\n",
911 targetpath, nt_errstr(status)));
912 errno = 0;
913 return -1;
916 status = cli_query_secdesc(targetcli, fnum, ctx, &sd);
917 if (!NT_STATUS_IS_OK(status)) {
918 DEBUG(5,("cacl_get Failed to query old descriptor "
919 "of %s: %s\n",
920 targetpath, nt_errstr(status)));
921 errno = 0;
922 return -1;
925 cli_close(targetcli, fnum);
927 if (! exclude_nt_revision) {
928 if (all || all_nt) {
929 if (determine_size) {
930 p = talloc_asprintf(ctx,
931 "REVISION:%d",
932 sd->revision);
933 if (!p) {
934 errno = ENOMEM;
935 return -1;
937 n = strlen(p);
938 } else {
939 n = snprintf(buf, bufsize,
940 "REVISION:%d",
941 sd->revision);
943 } else if (strcasecmp_m(name, "revision") == 0) {
944 if (determine_size) {
945 p = talloc_asprintf(ctx, "%d",
946 sd->revision);
947 if (!p) {
948 errno = ENOMEM;
949 return -1;
951 n = strlen(p);
952 } else {
953 n = snprintf(buf, bufsize, "%d",
954 sd->revision);
958 if (!determine_size && n > bufsize) {
959 errno = ERANGE;
960 return -1;
962 buf += n;
963 n_used += n;
964 bufsize -= n;
965 n = 0;
968 if (! exclude_nt_owner) {
969 /* Get owner and group sid */
970 if (sd->owner_sid) {
971 convert_sid_to_string(ipc_cli, pol,
972 sidstr,
973 numeric,
974 sd->owner_sid);
975 } else {
976 fstrcpy(sidstr, "");
979 if (all || all_nt) {
980 if (determine_size) {
981 p = talloc_asprintf(ctx, ",OWNER:%s",
982 sidstr);
983 if (!p) {
984 errno = ENOMEM;
985 return -1;
987 n = strlen(p);
988 } else if (sidstr[0] != '\0') {
989 n = snprintf(buf, bufsize,
990 ",OWNER:%s", sidstr);
992 } else if (strncasecmp_m(name, "owner", 5) == 0) {
993 if (determine_size) {
994 p = talloc_asprintf(ctx, "%s", sidstr);
995 if (!p) {
996 errno = ENOMEM;
997 return -1;
999 n = strlen(p);
1000 } else {
1001 n = snprintf(buf, bufsize, "%s",
1002 sidstr);
1006 if (!determine_size && n > bufsize) {
1007 errno = ERANGE;
1008 return -1;
1010 buf += n;
1011 n_used += n;
1012 bufsize -= n;
1013 n = 0;
1016 if (! exclude_nt_group) {
1017 if (sd->group_sid) {
1018 convert_sid_to_string(ipc_cli, pol,
1019 sidstr, numeric,
1020 sd->group_sid);
1021 } else {
1022 fstrcpy(sidstr, "");
1025 if (all || all_nt) {
1026 if (determine_size) {
1027 p = talloc_asprintf(ctx, ",GROUP:%s",
1028 sidstr);
1029 if (!p) {
1030 errno = ENOMEM;
1031 return -1;
1033 n = strlen(p);
1034 } else if (sidstr[0] != '\0') {
1035 n = snprintf(buf, bufsize,
1036 ",GROUP:%s", sidstr);
1038 } else if (strncasecmp_m(name, "group", 5) == 0) {
1039 if (determine_size) {
1040 p = talloc_asprintf(ctx, "%s", sidstr);
1041 if (!p) {
1042 errno = ENOMEM;
1043 return -1;
1045 n = strlen(p);
1046 } else {
1047 n = snprintf(buf, bufsize,
1048 "%s", sidstr);
1052 if (!determine_size && n > bufsize) {
1053 errno = ERANGE;
1054 return -1;
1056 buf += n;
1057 n_used += n;
1058 bufsize -= n;
1059 n = 0;
1062 if (! exclude_nt_acl) {
1063 /* Add aces to value buffer */
1064 for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
1066 struct security_ace *ace = &sd->dacl->aces[i];
1067 convert_sid_to_string(ipc_cli, pol,
1068 sidstr, numeric,
1069 &ace->trustee);
1071 if (all || all_nt) {
1072 if (determine_size) {
1073 p = talloc_asprintf(
1074 ctx,
1075 ",ACL:"
1076 "%s:%d/%d/0x%08x",
1077 sidstr,
1078 ace->type,
1079 ace->flags,
1080 ace->access_mask);
1081 if (!p) {
1082 errno = ENOMEM;
1083 return -1;
1085 n = strlen(p);
1086 } else {
1087 n = snprintf(
1088 buf, bufsize,
1089 ",ACL:%s:%d/%d/0x%08x",
1090 sidstr,
1091 ace->type,
1092 ace->flags,
1093 ace->access_mask);
1095 } else if ((strncasecmp_m(name, "acl", 3) == 0 &&
1096 strcasecmp_m(name+3, sidstr) == 0) ||
1097 (strncasecmp_m(name, "acl+", 4) == 0 &&
1098 strcasecmp_m(name+4, sidstr) == 0)) {
1099 if (determine_size) {
1100 p = talloc_asprintf(
1101 ctx,
1102 "%d/%d/0x%08x",
1103 ace->type,
1104 ace->flags,
1105 ace->access_mask);
1106 if (!p) {
1107 errno = ENOMEM;
1108 return -1;
1110 n = strlen(p);
1111 } else {
1112 n = snprintf(buf, bufsize,
1113 "%d/%d/0x%08x",
1114 ace->type,
1115 ace->flags,
1116 ace->access_mask);
1118 } else if (all_nt_acls) {
1119 if (determine_size) {
1120 p = talloc_asprintf(
1121 ctx,
1122 "%s%s:%d/%d/0x%08x",
1123 i ? "," : "",
1124 sidstr,
1125 ace->type,
1126 ace->flags,
1127 ace->access_mask);
1128 if (!p) {
1129 errno = ENOMEM;
1130 return -1;
1132 n = strlen(p);
1133 } else {
1134 n = snprintf(buf, bufsize,
1135 "%s%s:%d/%d/0x%08x",
1136 i ? "," : "",
1137 sidstr,
1138 ace->type,
1139 ace->flags,
1140 ace->access_mask);
1143 if (!determine_size && n > bufsize) {
1144 errno = ERANGE;
1145 return -1;
1147 buf += n;
1148 n_used += n;
1149 bufsize -= n;
1150 n = 0;
1154 /* Restore name pointer to its original value */
1155 name -= 19;
1158 if (all || some_dos) {
1159 /* Point to the portion after "system.dos_attr." */
1160 name += 16; /* if (all) this will be invalid but unused */
1162 /* Obtain the DOS attributes */
1163 if (!SMBC_getatr(context, srv, filename, &mode, &size,
1164 &create_time_ts,
1165 &access_time_ts,
1166 &write_time_ts,
1167 &change_time_ts,
1168 &ino)) {
1170 errno = SMBC_errno(context, srv->cli);
1171 return -1;
1174 create_time = convert_timespec_to_time_t(create_time_ts);
1175 access_time = convert_timespec_to_time_t(access_time_ts);
1176 write_time = convert_timespec_to_time_t(write_time_ts);
1177 change_time = convert_timespec_to_time_t(change_time_ts);
1179 if (! exclude_dos_mode) {
1180 if (all || all_dos) {
1181 if (determine_size) {
1182 p = talloc_asprintf(ctx,
1183 "%sMODE:0x%x",
1184 (ipc_cli &&
1185 (all || some_nt)
1186 ? ","
1187 : ""),
1188 mode);
1189 if (!p) {
1190 errno = ENOMEM;
1191 return -1;
1193 n = strlen(p);
1194 } else {
1195 n = snprintf(buf, bufsize,
1196 "%sMODE:0x%x",
1197 (ipc_cli &&
1198 (all || some_nt)
1199 ? ","
1200 : ""),
1201 mode);
1203 } else if (strcasecmp_m(name, "mode") == 0) {
1204 if (determine_size) {
1205 p = talloc_asprintf(ctx, "0x%x", mode);
1206 if (!p) {
1207 errno = ENOMEM;
1208 return -1;
1210 n = strlen(p);
1211 } else {
1212 n = snprintf(buf, bufsize,
1213 "0x%x", mode);
1217 if (!determine_size && n > bufsize) {
1218 errno = ERANGE;
1219 return -1;
1221 buf += n;
1222 n_used += n;
1223 bufsize -= n;
1224 n = 0;
1227 if (! exclude_dos_size) {
1228 if (all || all_dos) {
1229 if (determine_size) {
1230 p = talloc_asprintf(
1231 ctx,
1232 ",SIZE:%.0f",
1233 (double)size);
1234 if (!p) {
1235 errno = ENOMEM;
1236 return -1;
1238 n = strlen(p);
1239 } else {
1240 n = snprintf(buf, bufsize,
1241 ",SIZE:%.0f",
1242 (double)size);
1244 } else if (strcasecmp_m(name, "size") == 0) {
1245 if (determine_size) {
1246 p = talloc_asprintf(
1247 ctx,
1248 "%.0f",
1249 (double)size);
1250 if (!p) {
1251 errno = ENOMEM;
1252 return -1;
1254 n = strlen(p);
1255 } else {
1256 n = snprintf(buf, bufsize,
1257 "%.0f",
1258 (double)size);
1262 if (!determine_size && n > bufsize) {
1263 errno = ERANGE;
1264 return -1;
1266 buf += n;
1267 n_used += n;
1268 bufsize -= n;
1269 n = 0;
1272 if (! exclude_dos_create_time &&
1273 attr_strings.create_time_attr != NULL) {
1274 if (all || all_dos) {
1275 if (determine_size) {
1276 p = talloc_asprintf(ctx,
1277 ",%s:%lu",
1278 attr_strings.create_time_attr,
1279 (unsigned long) create_time);
1280 if (!p) {
1281 errno = ENOMEM;
1282 return -1;
1284 n = strlen(p);
1285 } else {
1286 n = snprintf(buf, bufsize,
1287 ",%s:%lu",
1288 attr_strings.create_time_attr,
1289 (unsigned long) create_time);
1291 } else if (strcasecmp_m(name, attr_strings.create_time_attr) == 0) {
1292 if (determine_size) {
1293 p = talloc_asprintf(ctx, "%lu", (unsigned long) create_time);
1294 if (!p) {
1295 errno = ENOMEM;
1296 return -1;
1298 n = strlen(p);
1299 } else {
1300 n = snprintf(buf, bufsize,
1301 "%lu", (unsigned long) create_time);
1305 if (!determine_size && n > bufsize) {
1306 errno = ERANGE;
1307 return -1;
1309 buf += n;
1310 n_used += n;
1311 bufsize -= n;
1312 n = 0;
1315 if (! exclude_dos_access_time) {
1316 if (all || all_dos) {
1317 if (determine_size) {
1318 p = talloc_asprintf(ctx,
1319 ",%s:%lu",
1320 attr_strings.access_time_attr,
1321 (unsigned long) access_time);
1322 if (!p) {
1323 errno = ENOMEM;
1324 return -1;
1326 n = strlen(p);
1327 } else {
1328 n = snprintf(buf, bufsize,
1329 ",%s:%lu",
1330 attr_strings.access_time_attr,
1331 (unsigned long) access_time);
1333 } else if (strcasecmp_m(name, attr_strings.access_time_attr) == 0) {
1334 if (determine_size) {
1335 p = talloc_asprintf(ctx, "%lu", (unsigned long) access_time);
1336 if (!p) {
1337 errno = ENOMEM;
1338 return -1;
1340 n = strlen(p);
1341 } else {
1342 n = snprintf(buf, bufsize,
1343 "%lu", (unsigned long) access_time);
1347 if (!determine_size && n > bufsize) {
1348 errno = ERANGE;
1349 return -1;
1351 buf += n;
1352 n_used += n;
1353 bufsize -= n;
1354 n = 0;
1357 if (! exclude_dos_write_time) {
1358 if (all || all_dos) {
1359 if (determine_size) {
1360 p = talloc_asprintf(ctx,
1361 ",%s:%lu",
1362 attr_strings.write_time_attr,
1363 (unsigned long) write_time);
1364 if (!p) {
1365 errno = ENOMEM;
1366 return -1;
1368 n = strlen(p);
1369 } else {
1370 n = snprintf(buf, bufsize,
1371 ",%s:%lu",
1372 attr_strings.write_time_attr,
1373 (unsigned long) write_time);
1375 } else if (strcasecmp_m(name, attr_strings.write_time_attr) == 0) {
1376 if (determine_size) {
1377 p = talloc_asprintf(ctx, "%lu", (unsigned long) write_time);
1378 if (!p) {
1379 errno = ENOMEM;
1380 return -1;
1382 n = strlen(p);
1383 } else {
1384 n = snprintf(buf, bufsize,
1385 "%lu", (unsigned long) write_time);
1389 if (!determine_size && n > bufsize) {
1390 errno = ERANGE;
1391 return -1;
1393 buf += n;
1394 n_used += n;
1395 bufsize -= n;
1396 n = 0;
1399 if (! exclude_dos_change_time) {
1400 if (all || all_dos) {
1401 if (determine_size) {
1402 p = talloc_asprintf(ctx,
1403 ",%s:%lu",
1404 attr_strings.change_time_attr,
1405 (unsigned long) change_time);
1406 if (!p) {
1407 errno = ENOMEM;
1408 return -1;
1410 n = strlen(p);
1411 } else {
1412 n = snprintf(buf, bufsize,
1413 ",%s:%lu",
1414 attr_strings.change_time_attr,
1415 (unsigned long) change_time);
1417 } else if (strcasecmp_m(name, attr_strings.change_time_attr) == 0) {
1418 if (determine_size) {
1419 p = talloc_asprintf(ctx, "%lu", (unsigned long) change_time);
1420 if (!p) {
1421 errno = ENOMEM;
1422 return -1;
1424 n = strlen(p);
1425 } else {
1426 n = snprintf(buf, bufsize,
1427 "%lu", (unsigned long) change_time);
1431 if (!determine_size && n > bufsize) {
1432 errno = ERANGE;
1433 return -1;
1435 buf += n;
1436 n_used += n;
1437 bufsize -= n;
1438 n = 0;
1441 if (! exclude_dos_inode) {
1442 if (all || all_dos) {
1443 if (determine_size) {
1444 p = talloc_asprintf(
1445 ctx,
1446 ",INODE:%.0f",
1447 (double)ino);
1448 if (!p) {
1449 errno = ENOMEM;
1450 return -1;
1452 n = strlen(p);
1453 } else {
1454 n = snprintf(buf, bufsize,
1455 ",INODE:%.0f",
1456 (double) ino);
1458 } else if (strcasecmp_m(name, "inode") == 0) {
1459 if (determine_size) {
1460 p = talloc_asprintf(
1461 ctx,
1462 "%.0f",
1463 (double) ino);
1464 if (!p) {
1465 errno = ENOMEM;
1466 return -1;
1468 n = strlen(p);
1469 } else {
1470 n = snprintf(buf, bufsize,
1471 "%.0f",
1472 (double) ino);
1476 if (!determine_size && n > bufsize) {
1477 errno = ERANGE;
1478 return -1;
1480 buf += n;
1481 n_used += n;
1482 bufsize -= n;
1483 n = 0;
1486 /* Restore name pointer to its original value */
1487 name -= 16;
1490 if (n_used == 0) {
1491 errno = ENOATTR;
1492 return -1;
1495 return n_used;
1498 /*****************************************************
1499 set the ACLs on a file given an ascii description
1500 *******************************************************/
1501 static int
1502 cacl_set(SMBCCTX *context,
1503 TALLOC_CTX *ctx,
1504 struct cli_state *cli,
1505 struct cli_state *ipc_cli,
1506 struct policy_handle *pol,
1507 const char *filename,
1508 char *the_acl,
1509 int mode,
1510 int flags)
1512 uint16_t fnum = (uint16_t)-1;
1513 int err = 0;
1514 struct security_descriptor *sd = NULL, *old;
1515 struct security_acl *dacl = NULL;
1516 struct dom_sid *owner_sid = NULL;
1517 struct dom_sid *group_sid = NULL;
1518 uint32 i, j;
1519 size_t sd_size;
1520 int ret = 0;
1521 char *p;
1522 bool numeric = True;
1523 char *targetpath = NULL;
1524 struct cli_state *targetcli = NULL;
1525 NTSTATUS status;
1527 /* the_acl will be null for REMOVE_ALL operations */
1528 if (the_acl) {
1529 numeric = ((p = strchr(the_acl, ':')) != NULL &&
1530 p > the_acl &&
1531 p[-1] != '+');
1533 /* if this is to set the entire ACL... */
1534 if (*the_acl == '*') {
1535 /* ... then increment past the first colon */
1536 the_acl = p + 1;
1539 sd = sec_desc_parse(ctx, ipc_cli, pol, numeric, the_acl);
1540 if (!sd) {
1541 errno = EINVAL;
1542 return -1;
1546 /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
1547 that doesn't deref sd */
1549 if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
1550 errno = EINVAL;
1551 return -1;
1554 status = cli_resolve_path(ctx, "", context->internal->auth_info,
1555 cli, filename, &targetcli, &targetpath);
1556 if (!NT_STATUS_IS_OK(status)) {
1557 DEBUG(5,("cacl_set: Could not resolve %s\n", filename));
1558 errno = ENOENT;
1559 return -1;
1562 /* The desired access below is the only one I could find that works
1563 with NT4, W2KP and Samba */
1565 status = cli_ntcreate(targetcli, targetpath, 0, CREATE_ACCESS_READ, 0,
1566 FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
1567 0x0, 0x0, &fnum, NULL);
1568 if (!NT_STATUS_IS_OK(status)) {
1569 DEBUG(5, ("cacl_set failed to open %s: %s\n",
1570 targetpath, nt_errstr(status)));
1571 errno = 0;
1572 return -1;
1575 status = cli_query_secdesc(targetcli, fnum, ctx, &old);
1576 if (!NT_STATUS_IS_OK(status)) {
1577 DEBUG(5,("cacl_set Failed to query old descriptor of %s: %s\n",
1578 targetpath, nt_errstr(status)));
1579 errno = 0;
1580 return -1;
1583 cli_close(targetcli, fnum);
1585 switch (mode) {
1586 case SMBC_XATTR_MODE_REMOVE_ALL:
1587 old->dacl->num_aces = 0;
1588 dacl = old->dacl;
1589 break;
1591 case SMBC_XATTR_MODE_REMOVE:
1592 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
1593 bool found = False;
1595 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
1596 if (security_ace_equal(&sd->dacl->aces[i],
1597 &old->dacl->aces[j])) {
1598 uint32 k;
1599 for (k=j; k<old->dacl->num_aces-1;k++) {
1600 old->dacl->aces[k] =
1601 old->dacl->aces[k+1];
1603 old->dacl->num_aces--;
1604 found = True;
1605 dacl = old->dacl;
1606 break;
1610 if (!found) {
1611 err = ENOATTR;
1612 ret = -1;
1613 goto failed;
1616 break;
1618 case SMBC_XATTR_MODE_ADD:
1619 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
1620 bool found = False;
1622 for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
1623 if (dom_sid_equal(&sd->dacl->aces[i].trustee,
1624 &old->dacl->aces[j].trustee)) {
1625 if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
1626 err = EEXIST;
1627 ret = -1;
1628 goto failed;
1630 old->dacl->aces[j] = sd->dacl->aces[i];
1631 ret = -1;
1632 found = True;
1636 if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
1637 err = ENOATTR;
1638 ret = -1;
1639 goto failed;
1642 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
1643 add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
1646 dacl = old->dacl;
1647 break;
1649 case SMBC_XATTR_MODE_SET:
1650 old = sd;
1651 owner_sid = old->owner_sid;
1652 group_sid = old->group_sid;
1653 dacl = old->dacl;
1654 break;
1656 case SMBC_XATTR_MODE_CHOWN:
1657 owner_sid = sd->owner_sid;
1658 break;
1660 case SMBC_XATTR_MODE_CHGRP:
1661 group_sid = sd->group_sid;
1662 break;
1665 /* Denied ACE entries must come before allowed ones */
1666 sort_acl(old->dacl);
1668 /* Create new security descriptor and set it */
1669 sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
1670 owner_sid, group_sid, NULL, dacl, &sd_size);
1672 status = cli_ntcreate(targetcli, targetpath, 0,
1673 WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS, 0,
1674 FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
1675 0x0, 0x0, &fnum, NULL);
1676 if (!NT_STATUS_IS_OK(status)) {
1677 DEBUG(5, ("cacl_set failed to open %s: %s\n",
1678 targetpath, nt_errstr(status)));
1679 errno = 0;
1680 return -1;
1683 status = cli_set_secdesc(targetcli, fnum, sd);
1684 if (!NT_STATUS_IS_OK(status)) {
1685 DEBUG(5, ("ERROR: secdesc set failed: %s\n",
1686 nt_errstr(status)));
1687 ret = -1;
1690 /* Clean up */
1692 failed:
1693 cli_close(targetcli, fnum);
1695 if (err != 0) {
1696 errno = err;
1699 return ret;
1704 SMBC_setxattr_ctx(SMBCCTX *context,
1705 const char *fname,
1706 const char *name,
1707 const void *value,
1708 size_t size,
1709 int flags)
1711 int ret;
1712 int ret2;
1713 SMBCSRV *srv = NULL;
1714 SMBCSRV *ipc_srv = NULL;
1715 char *server = NULL;
1716 char *share = NULL;
1717 char *user = NULL;
1718 char *password = NULL;
1719 char *workgroup = NULL;
1720 char *path = NULL;
1721 DOS_ATTR_DESC *dad = NULL;
1722 struct {
1723 const char * create_time_attr;
1724 const char * access_time_attr;
1725 const char * write_time_attr;
1726 const char * change_time_attr;
1727 } attr_strings;
1728 uint16_t port = 0;
1729 TALLOC_CTX *frame = talloc_stackframe();
1731 if (!context || !context->internal->initialized) {
1732 errno = EINVAL; /* Best I can think of ... */
1733 TALLOC_FREE(frame);
1734 return -1;
1737 if (!fname) {
1738 errno = EINVAL;
1739 TALLOC_FREE(frame);
1740 return -1;
1743 DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
1744 fname, name, (int) size, (const char*)value));
1746 if (SMBC_parse_path(frame,
1747 context,
1748 fname,
1749 &workgroup,
1750 &server,
1751 &port,
1752 &share,
1753 &path,
1754 &user,
1755 &password,
1756 NULL)) {
1757 errno = EINVAL;
1758 TALLOC_FREE(frame);
1759 return -1;
1762 if (!user || user[0] == (char)0) {
1763 user = talloc_strdup(frame, smbc_getUser(context));
1764 if (!user) {
1765 errno = ENOMEM;
1766 TALLOC_FREE(frame);
1767 return -1;
1771 srv = SMBC_server(frame, context, True,
1772 server, port, share, &workgroup, &user, &password);
1773 if (!srv) {
1774 TALLOC_FREE(frame);
1775 return -1; /* errno set by SMBC_server */
1778 if (! srv->no_nt_session) {
1779 ipc_srv = SMBC_attr_server(frame, context, server, port, share,
1780 &workgroup, &user, &password);
1781 if (! ipc_srv) {
1782 srv->no_nt_session = True;
1784 } else {
1785 ipc_srv = NULL;
1789 * Are they asking to set the entire set of known attributes?
1791 if (strcasecmp_m(name, "system.*") == 0 ||
1792 strcasecmp_m(name, "system.*+") == 0) {
1793 /* Yup. */
1794 char *namevalue =
1795 talloc_asprintf(talloc_tos(), "%s:%s",
1796 name+7, (const char *) value);
1797 if (! namevalue) {
1798 errno = ENOMEM;
1799 ret = -1;
1800 TALLOC_FREE(frame);
1801 return -1;
1804 if (ipc_srv) {
1805 ret = cacl_set(context, talloc_tos(), srv->cli,
1806 ipc_srv->cli, &ipc_srv->pol, path,
1807 namevalue,
1808 (*namevalue == '*'
1809 ? SMBC_XATTR_MODE_SET
1810 : SMBC_XATTR_MODE_ADD),
1811 flags);
1812 } else {
1813 ret = 0;
1816 /* get a DOS Attribute Descriptor with current attributes */
1817 dad = dos_attr_query(context, talloc_tos(), path, srv);
1818 if (dad) {
1819 /* Overwrite old with new, using what was provided */
1820 dos_attr_parse(context, dad, srv, namevalue);
1822 /* Set the new DOS attributes */
1823 if (! SMBC_setatr(context, srv, path,
1824 dad->create_time,
1825 dad->access_time,
1826 dad->write_time,
1827 dad->change_time,
1828 dad->mode)) {
1830 /* cause failure if NT failed too */
1831 dad = NULL;
1835 /* we only fail if both NT and DOS sets failed */
1836 if (ret < 0 && ! dad) {
1837 ret = -1; /* in case dad was null */
1839 else {
1840 ret = 0;
1843 TALLOC_FREE(frame);
1844 return ret;
1848 * Are they asking to set an access control element or to set
1849 * the entire access control list?
1851 if (strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
1852 strcasecmp_m(name, "system.nt_sec_desc.*+") == 0 ||
1853 strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
1854 strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
1855 strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0) {
1857 /* Yup. */
1858 char *namevalue =
1859 talloc_asprintf(talloc_tos(), "%s:%s",
1860 name+19, (const char *) value);
1862 if (! ipc_srv) {
1863 ret = -1; /* errno set by SMBC_server() */
1865 else if (! namevalue) {
1866 errno = ENOMEM;
1867 ret = -1;
1868 } else {
1869 ret = cacl_set(context, talloc_tos(), srv->cli,
1870 ipc_srv->cli, &ipc_srv->pol, path,
1871 namevalue,
1872 (*namevalue == '*'
1873 ? SMBC_XATTR_MODE_SET
1874 : SMBC_XATTR_MODE_ADD),
1875 flags);
1877 TALLOC_FREE(frame);
1878 return ret;
1882 * Are they asking to set the owner?
1884 if (strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
1885 strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0) {
1887 /* Yup. */
1888 char *namevalue =
1889 talloc_asprintf(talloc_tos(), "%s:%s",
1890 name+19, (const char *) value);
1892 if (! ipc_srv) {
1893 ret = -1; /* errno set by SMBC_server() */
1895 else if (! namevalue) {
1896 errno = ENOMEM;
1897 ret = -1;
1898 } else {
1899 ret = cacl_set(context, talloc_tos(), srv->cli,
1900 ipc_srv->cli, &ipc_srv->pol, path,
1901 namevalue, SMBC_XATTR_MODE_CHOWN, 0);
1903 TALLOC_FREE(frame);
1904 return ret;
1908 * Are they asking to set the group?
1910 if (strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
1911 strcasecmp_m(name, "system.nt_sec_desc.group+") == 0) {
1913 /* Yup. */
1914 char *namevalue =
1915 talloc_asprintf(talloc_tos(), "%s:%s",
1916 name+19, (const char *) value);
1918 if (! ipc_srv) {
1919 /* errno set by SMBC_server() */
1920 ret = -1;
1922 else if (! namevalue) {
1923 errno = ENOMEM;
1924 ret = -1;
1925 } else {
1926 ret = cacl_set(context, talloc_tos(), srv->cli,
1927 ipc_srv->cli, &ipc_srv->pol, path,
1928 namevalue, SMBC_XATTR_MODE_CHGRP, 0);
1930 TALLOC_FREE(frame);
1931 return ret;
1934 /* Determine whether to use old-style or new-style attribute names */
1935 if (context->internal->full_time_names) {
1936 /* new-style names */
1937 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
1938 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
1939 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
1940 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
1941 } else {
1942 /* old-style names */
1943 attr_strings.create_time_attr = NULL;
1944 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
1945 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
1946 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
1950 * Are they asking to set a DOS attribute?
1952 if (strcasecmp_m(name, "system.dos_attr.*") == 0 ||
1953 strcasecmp_m(name, "system.dos_attr.mode") == 0 ||
1954 (attr_strings.create_time_attr != NULL &&
1955 strcasecmp_m(name, attr_strings.create_time_attr) == 0) ||
1956 strcasecmp_m(name, attr_strings.access_time_attr) == 0 ||
1957 strcasecmp_m(name, attr_strings.write_time_attr) == 0 ||
1958 strcasecmp_m(name, attr_strings.change_time_attr) == 0) {
1960 /* get a DOS Attribute Descriptor with current attributes */
1961 dad = dos_attr_query(context, talloc_tos(), path, srv);
1962 if (dad) {
1963 char *namevalue =
1964 talloc_asprintf(talloc_tos(), "%s:%s",
1965 name+16, (const char *) value);
1966 if (! namevalue) {
1967 errno = ENOMEM;
1968 ret = -1;
1969 } else {
1970 /* Overwrite old with provided new params */
1971 dos_attr_parse(context, dad, srv, namevalue);
1973 /* Set the new DOS attributes */
1974 ret2 = SMBC_setatr(context, srv, path,
1975 dad->create_time,
1976 dad->access_time,
1977 dad->write_time,
1978 dad->change_time,
1979 dad->mode);
1981 /* ret2 has True (success) / False (failure) */
1982 if (ret2) {
1983 ret = 0;
1984 } else {
1985 ret = -1;
1988 } else {
1989 ret = -1;
1992 TALLOC_FREE(frame);
1993 return ret;
1996 /* Unsupported attribute name */
1997 errno = EINVAL;
1998 TALLOC_FREE(frame);
1999 return -1;
2003 SMBC_getxattr_ctx(SMBCCTX *context,
2004 const char *fname,
2005 const char *name,
2006 const void *value,
2007 size_t size)
2009 int ret;
2010 SMBCSRV *srv = NULL;
2011 SMBCSRV *ipc_srv = NULL;
2012 char *server = NULL;
2013 char *share = NULL;
2014 char *user = NULL;
2015 char *password = NULL;
2016 char *workgroup = NULL;
2017 char *path = NULL;
2018 struct {
2019 const char * create_time_attr;
2020 const char * access_time_attr;
2021 const char * write_time_attr;
2022 const char * change_time_attr;
2023 } attr_strings;
2024 uint16_t port = 0;
2025 TALLOC_CTX *frame = talloc_stackframe();
2027 if (!context || !context->internal->initialized) {
2028 errno = EINVAL; /* Best I can think of ... */
2029 TALLOC_FREE(frame);
2030 return -1;
2033 if (!fname) {
2034 errno = EINVAL;
2035 TALLOC_FREE(frame);
2036 return -1;
2039 DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
2041 if (SMBC_parse_path(frame,
2042 context,
2043 fname,
2044 &workgroup,
2045 &server,
2046 &port,
2047 &share,
2048 &path,
2049 &user,
2050 &password,
2051 NULL)) {
2052 errno = EINVAL;
2053 TALLOC_FREE(frame);
2054 return -1;
2057 if (!user || user[0] == (char)0) {
2058 user = talloc_strdup(frame, smbc_getUser(context));
2059 if (!user) {
2060 errno = ENOMEM;
2061 TALLOC_FREE(frame);
2062 return -1;
2066 srv = SMBC_server(frame, context, True,
2067 server, port, share, &workgroup, &user, &password);
2068 if (!srv) {
2069 TALLOC_FREE(frame);
2070 return -1; /* errno set by SMBC_server */
2073 if (! srv->no_nt_session) {
2074 ipc_srv = SMBC_attr_server(frame, context, server, port, share,
2075 &workgroup, &user, &password);
2076 if (! ipc_srv) {
2077 srv->no_nt_session = True;
2079 } else {
2080 ipc_srv = NULL;
2083 /* Determine whether to use old-style or new-style attribute names */
2084 if (context->internal->full_time_names) {
2085 /* new-style names */
2086 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
2087 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
2088 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
2089 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
2090 } else {
2091 /* old-style names */
2092 attr_strings.create_time_attr = NULL;
2093 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
2094 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
2095 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
2098 /* Are they requesting a supported attribute? */
2099 if (strcasecmp_m(name, "system.*") == 0 ||
2100 strncasecmp_m(name, "system.*!", 9) == 0 ||
2101 strcasecmp_m(name, "system.*+") == 0 ||
2102 strncasecmp_m(name, "system.*+!", 10) == 0 ||
2103 strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
2104 strncasecmp_m(name, "system.nt_sec_desc.*!", 21) == 0 ||
2105 strcasecmp_m(name, "system.nt_sec_desc.*+") == 0 ||
2106 strncasecmp_m(name, "system.nt_sec_desc.*+!", 22) == 0 ||
2107 strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
2108 strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
2109 strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0 ||
2110 strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
2111 strcasecmp_m(name, "system.nt_sec_desc.group+") == 0 ||
2112 strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
2113 strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0 ||
2114 strcasecmp_m(name, "system.dos_attr.*") == 0 ||
2115 strncasecmp_m(name, "system.dos_attr.*!", 18) == 0 ||
2116 strcasecmp_m(name, "system.dos_attr.mode") == 0 ||
2117 strcasecmp_m(name, "system.dos_attr.size") == 0 ||
2118 (attr_strings.create_time_attr != NULL &&
2119 strcasecmp_m(name, attr_strings.create_time_attr) == 0) ||
2120 strcasecmp_m(name, attr_strings.access_time_attr) == 0 ||
2121 strcasecmp_m(name, attr_strings.write_time_attr) == 0 ||
2122 strcasecmp_m(name, attr_strings.change_time_attr) == 0 ||
2123 strcasecmp_m(name, "system.dos_attr.inode") == 0) {
2125 /* Yup. */
2126 const char *filename = name;
2127 ret = cacl_get(context, talloc_tos(), srv,
2128 ipc_srv == NULL ? NULL : ipc_srv->cli,
2129 &ipc_srv->pol, path,
2130 filename,
2131 discard_const_p(char, value),
2132 size);
2133 if (ret < 0 && errno == 0) {
2134 errno = SMBC_errno(context, srv->cli);
2136 TALLOC_FREE(frame);
2137 return ret;
2140 /* Unsupported attribute name */
2141 errno = EINVAL;
2142 TALLOC_FREE(frame);
2143 return -1;
2148 SMBC_removexattr_ctx(SMBCCTX *context,
2149 const char *fname,
2150 const char *name)
2152 int ret;
2153 SMBCSRV *srv = NULL;
2154 SMBCSRV *ipc_srv = NULL;
2155 char *server = NULL;
2156 char *share = NULL;
2157 char *user = NULL;
2158 char *password = NULL;
2159 char *workgroup = NULL;
2160 char *path = NULL;
2161 uint16_t port = 0;
2162 TALLOC_CTX *frame = talloc_stackframe();
2164 if (!context || !context->internal->initialized) {
2165 errno = EINVAL; /* Best I can think of ... */
2166 TALLOC_FREE(frame);
2167 return -1;
2170 if (!fname) {
2171 errno = EINVAL;
2172 TALLOC_FREE(frame);
2173 return -1;
2176 DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
2178 if (SMBC_parse_path(frame,
2179 context,
2180 fname,
2181 &workgroup,
2182 &server,
2183 &port,
2184 &share,
2185 &path,
2186 &user,
2187 &password,
2188 NULL)) {
2189 errno = EINVAL;
2190 TALLOC_FREE(frame);
2191 return -1;
2194 if (!user || user[0] == (char)0) {
2195 user = talloc_strdup(frame, smbc_getUser(context));
2196 if (!user) {
2197 errno = ENOMEM;
2198 TALLOC_FREE(frame);
2199 return -1;
2203 srv = SMBC_server(frame, context, True,
2204 server, port, share, &workgroup, &user, &password);
2205 if (!srv) {
2206 TALLOC_FREE(frame);
2207 return -1; /* errno set by SMBC_server */
2210 if (! srv->no_nt_session) {
2211 ipc_srv = SMBC_attr_server(frame, context, server, port, share,
2212 &workgroup, &user, &password);
2213 if (! ipc_srv) {
2214 srv->no_nt_session = True;
2216 } else {
2217 ipc_srv = NULL;
2220 if (! ipc_srv) {
2221 TALLOC_FREE(frame);
2222 return -1; /* errno set by SMBC_attr_server */
2225 /* Are they asking to set the entire ACL? */
2226 if (strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
2227 strcasecmp_m(name, "system.nt_sec_desc.*+") == 0) {
2229 /* Yup. */
2230 ret = cacl_set(context, talloc_tos(), srv->cli,
2231 ipc_srv->cli, &ipc_srv->pol, path,
2232 NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
2233 TALLOC_FREE(frame);
2234 return ret;
2238 * Are they asking to remove one or more spceific security descriptor
2239 * attributes?
2241 if (strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
2242 strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
2243 strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0 ||
2244 strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
2245 strcasecmp_m(name, "system.nt_sec_desc.group+") == 0 ||
2246 strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
2247 strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0) {
2249 /* Yup. */
2250 ret = cacl_set(context, talloc_tos(), srv->cli,
2251 ipc_srv->cli, &ipc_srv->pol, path,
2252 discard_const_p(char, name) + 19,
2253 SMBC_XATTR_MODE_REMOVE, 0);
2254 TALLOC_FREE(frame);
2255 return ret;
2258 /* Unsupported attribute name */
2259 errno = EINVAL;
2260 TALLOC_FREE(frame);
2261 return -1;
2265 SMBC_listxattr_ctx(SMBCCTX *context,
2266 const char *fname,
2267 char *list,
2268 size_t size)
2271 * This isn't quite what listxattr() is supposed to do. This returns
2272 * the complete set of attribute names, always, rather than only those
2273 * attribute names which actually exist for a file. Hmmm...
2275 size_t retsize;
2276 const char supported_old[] =
2277 "system.*\0"
2278 "system.*+\0"
2279 "system.nt_sec_desc.revision\0"
2280 "system.nt_sec_desc.owner\0"
2281 "system.nt_sec_desc.owner+\0"
2282 "system.nt_sec_desc.group\0"
2283 "system.nt_sec_desc.group+\0"
2284 "system.nt_sec_desc.acl.*\0"
2285 "system.nt_sec_desc.acl\0"
2286 "system.nt_sec_desc.acl+\0"
2287 "system.nt_sec_desc.*\0"
2288 "system.nt_sec_desc.*+\0"
2289 "system.dos_attr.*\0"
2290 "system.dos_attr.mode\0"
2291 "system.dos_attr.c_time\0"
2292 "system.dos_attr.a_time\0"
2293 "system.dos_attr.m_time\0"
2295 const char supported_new[] =
2296 "system.*\0"
2297 "system.*+\0"
2298 "system.nt_sec_desc.revision\0"
2299 "system.nt_sec_desc.owner\0"
2300 "system.nt_sec_desc.owner+\0"
2301 "system.nt_sec_desc.group\0"
2302 "system.nt_sec_desc.group+\0"
2303 "system.nt_sec_desc.acl.*\0"
2304 "system.nt_sec_desc.acl\0"
2305 "system.nt_sec_desc.acl+\0"
2306 "system.nt_sec_desc.*\0"
2307 "system.nt_sec_desc.*+\0"
2308 "system.dos_attr.*\0"
2309 "system.dos_attr.mode\0"
2310 "system.dos_attr.create_time\0"
2311 "system.dos_attr.access_time\0"
2312 "system.dos_attr.write_time\0"
2313 "system.dos_attr.change_time\0"
2315 const char * supported;
2317 if (context->internal->full_time_names) {
2318 supported = supported_new;
2319 retsize = sizeof(supported_new);
2320 } else {
2321 supported = supported_old;
2322 retsize = sizeof(supported_old);
2325 if (size == 0) {
2326 return retsize;
2329 if (retsize > size) {
2330 errno = ERANGE;
2331 return -1;
2334 /* this can't be strcpy() because there are embedded null characters */
2335 memcpy(list, supported, retsize);
2336 return retsize;