s4/torture: Fix misplaced positional arguments for u64 comparison
[gd/samba-autobuild/.git] / source3 / libsmb / libsmb_xattr.c
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
10
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.
15
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.
20
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/>.
23 */
24
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"
34
35 /*
36  * Find an lsa pipe handle associated with a cli struct.
37  */
38 static struct rpc_pipe_client *
39 find_lsa_pipe_hnd(struct cli_state *ipc_cli)
40 {
41         struct rpc_pipe_client *pipe_hnd;
42
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;
49                 }
50         }
51         return NULL;
52 }
53
54 /*
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.
58  */
59
60 static int
61 ace_compare(struct security_ace *ace1,
62             struct security_ace *ace2)
63 {
64         bool b1;
65         bool b2;
66
67         /* If the ACEs are equal, we have nothing more to do. */
68         if (security_ace_equal(ace1, ace2)) {
69                 return 0;
70         }
71
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);
77         }
78
79         /*
80          * What shall we do with AUDITs and ALARMs?  It's undefined.  We'll
81          * sort them after DENY and ALLOW.
82          */
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);
93         }
94
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);
102         }
103
104         /*
105          * ACEs applying to an entity's object follow those applying to the
106          * entity itself
107          */
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);
114         }
115
116         /*
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.
121          */
122
123         if (ace1->type != ace2->type) {
124                 return ace2->type - ace1->type;
125         }
126
127         if (dom_sid_compare(&ace1->trustee, &ace2->trustee)) {
128                 return dom_sid_compare(&ace1->trustee, &ace2->trustee);
129         }
130
131         if (ace1->flags != ace2->flags) {
132                 return ace1->flags - ace2->flags;
133         }
134
135         if (ace1->access_mask != ace2->access_mask) {
136                 return ace1->access_mask - ace2->access_mask;
137         }
138
139         if (ace1->size != ace2->size) {
140                 return ace1->size - ace2->size;
141         }
142
143         return memcmp(ace1, ace2, sizeof(struct security_ace));
144 }
145
146
147 static void
148 sort_acl(struct security_acl *the_acl)
149 {
150         uint32_t i;
151         if (!the_acl) return;
152
153         TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
154
155         for (i=1;i<the_acl->num_aces;) {
156                 if (security_ace_equal(&the_acl->aces[i-1],
157                                        &the_acl->aces[i])) {
158                         ARRAY_DEL_ELEMENT(
159                                 the_acl->aces, i, the_acl->num_aces);
160                         the_acl->num_aces--;
161                 } else {
162                         i++;
163                 }
164         }
165 }
166
167 /* convert a SID to a string, either numeric or username/group */
168 static void
169 convert_sid_to_string(struct cli_state *ipc_cli,
170                       struct policy_handle *pol,
171                       fstring str,
172                       bool numeric,
173                       struct dom_sid *sid)
174 {
175         char **domains = NULL;
176         char **names = NULL;
177         enum lsa_SidType *types = NULL;
178         struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
179         TALLOC_CTX *ctx;
180
181         sid_to_fstring(str, sid);
182
183         if (numeric) {
184                 return;     /* no lookup desired */
185         }
186
187         if (!pipe_hnd) {
188                 return;
189         }
190
191         /* Ask LSA to convert the sid to a name */
192
193         ctx = talloc_stackframe();
194
195         if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ctx,
196                                                     pol, 1, sid, &domains,
197                                                     &names, &types)) ||
198             !domains || !domains[0] || !names || !names[0]) {
199                 TALLOC_FREE(ctx);
200                 return;
201         }
202
203         /* Converted OK */
204
205         fstr_sprintf(str, "%s%s%s",
206                      domains[0], lp_winbind_separator(), names[0]);
207
208         TALLOC_FREE(ctx);
209 }
210
211 /* convert a string to a SID, either numeric or username/group */
212 static bool
213 convert_string_to_sid(struct cli_state *ipc_cli,
214                       struct policy_handle *pol,
215                       bool numeric,
216                       struct dom_sid *sid,
217                       const char *str)
218 {
219         enum lsa_SidType *types = NULL;
220         struct dom_sid *sids = NULL;
221         bool result = True;
222         TALLOC_CTX *ctx = NULL;
223         struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
224
225         if (!pipe_hnd) {
226                 return False;
227         }
228
229         if (numeric) {
230                 if (strncmp(str, "S-", 2) == 0) {
231                         return string_to_sid(sid, str);
232                 }
233
234                 result = False;
235                 goto done;
236         }
237
238         ctx = talloc_stackframe();
239         if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ctx,
240                                                      pol, 1, &str,
241                                                      NULL, 1, &sids,
242                                                      &types))) {
243                 result = False;
244                 goto done;
245         }
246
247         sid_copy(sid, &sids[0]);
248 done:
249         TALLOC_FREE(ctx);
250         return result;
251 }
252
253
254 /* parse an struct security_ace in the same format as print_ace() */
255 static bool
256 parse_ace(struct cli_state *ipc_cli,
257           struct policy_handle *pol,
258           struct security_ace *ace,
259           bool numeric,
260           char *str)
261 {
262         char *p;
263         const char *cp;
264         char *tok;
265         unsigned int atype;
266         unsigned int aflags;
267         unsigned int amask;
268         struct dom_sid sid;
269         uint32_t mask;
270         struct perm_value {
271                 const char perm[7];
272                 uint32_t mask;
273         };
274         size_t i;
275         TALLOC_CTX *frame = talloc_stackframe();
276
277         /* These values discovered by inspection */
278         static const struct perm_value special_values[] = {
279                 { "R", 0x00120089 },
280                 { "W", 0x00120116 },
281                 { "X", 0x001200a0 },
282                 { "D", 0x00010000 },
283                 { "P", 0x00040000 },
284                 { "O", 0x00080000 },
285         };
286
287         static const struct perm_value standard_values[] = {
288                 { "READ",   0x001200a9 },
289                 { "CHANGE", 0x001301bf },
290                 { "FULL",   0x001f01ff },
291         };
292
293         ZERO_STRUCTP(ace);
294         p = strchr_m(str,':');
295         if (!p) {
296                 TALLOC_FREE(frame);
297                 return False;
298         }
299         *p = '\0';
300         p++;
301         /* Try to parse numeric form */
302
303         if (sscanf(p, "%u/%u/%u", &atype, &aflags, &amask) == 3 &&
304             convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
305                 goto done;
306         }
307
308         /* Try to parse text form */
309
310         if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
311                 TALLOC_FREE(frame);
312                 return false;
313         }
314
315         cp = p;
316         if (!next_token_talloc(frame, &cp, &tok, "/")) {
317                 TALLOC_FREE(frame);
318                 return false;
319         }
320
321         if (strncasecmp_m(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
322                 atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
323         } else if (strncasecmp_m(tok, "DENIED", strlen("DENIED")) == 0) {
324                 atype = SEC_ACE_TYPE_ACCESS_DENIED;
325         } else {
326                 TALLOC_FREE(frame);
327                 return false;
328         }
329
330         /* Only numeric form accepted for flags at present */
331
332         if (!(next_token_talloc(frame, &cp, &tok, "/") &&
333               sscanf(tok, "%u", &aflags))) {
334                 TALLOC_FREE(frame);
335                 return false;
336         }
337
338         if (!next_token_talloc(frame, &cp, &tok, "/")) {
339                 TALLOC_FREE(frame);
340                 return false;
341         }
342
343         if (strncmp(tok, "0x", 2) == 0) {
344                 if (sscanf(tok, "%u", &amask) != 1) {
345                         TALLOC_FREE(frame);
346                         return false;
347                 }
348                 goto done;
349         }
350
351         for (i = 0; i < ARRAY_SIZE(standard_values); i++) {
352                 const struct perm_value *v = &standard_values[i];
353                 if (strcmp(tok, v->perm) == 0) {
354                         amask = v->mask;
355                         goto done;
356                 }
357         }
358
359         p = tok;
360
361         while(*p) {
362                 bool found = False;
363
364                 for (i = 0; i < ARRAY_SIZE(special_values); i++) {
365                         const struct perm_value *v = &special_values[i];
366                         if (v->perm[0] == *p) {
367                                 amask |= v->mask;
368                                 found = True;
369                         }
370                 }
371
372                 if (!found) {
373                         TALLOC_FREE(frame);
374                         return false;
375                 }
376                 p++;
377         }
378
379         if (*p) {
380                 TALLOC_FREE(frame);
381                 return false;
382         }
383
384 done:
385         mask = amask;
386         init_sec_ace(ace, &sid, atype, mask, aflags);
387         TALLOC_FREE(frame);
388         return true;
389 }
390
391 /* add an struct security_ace to a list of struct security_aces in a struct security_acl */
392 static bool
393 add_ace(struct security_acl **the_acl,
394         const struct security_ace *ace,
395         TALLOC_CTX *ctx)
396 {
397         struct security_acl *acl = *the_acl;
398
399         if (acl == NULL) {
400                 acl = make_sec_acl(ctx, 3, 0, NULL);
401                 if (acl == NULL) {
402                         return false;
403                 }
404         }
405
406         if (acl->num_aces == UINT32_MAX) {
407                 return false;
408         }
409         ADD_TO_ARRAY(
410                 acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
411         *the_acl = acl;
412         return True;
413 }
414
415
416 /* parse a ascii version of a security descriptor */
417 static struct security_descriptor *
418 sec_desc_parse(TALLOC_CTX *ctx,
419                struct cli_state *ipc_cli,
420                struct policy_handle *pol,
421                bool numeric,
422                const char *str)
423 {
424         const char *p = str;
425         char *tok;
426         struct security_descriptor *ret = NULL;
427         size_t sd_size;
428         struct dom_sid owner_sid = { .num_auths = 0 };
429         struct dom_sid group_sid = { .num_auths = 0 };
430         bool have_owner = false, have_group = false;
431         struct security_acl *dacl=NULL;
432         int revision=1;
433
434         while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
435
436                 if (strncasecmp_m(tok,"REVISION:", 9) == 0) {
437                         revision = strtol(tok+9, NULL, 16);
438                         continue;
439                 }
440
441                 if (strncasecmp_m(tok,"OWNER:", 6) == 0) {
442                         if (have_owner) {
443                                 DEBUG(5,("OWNER specified more than once!\n"));
444                                 goto done;
445                         }
446                         if (!convert_string_to_sid(ipc_cli, pol,
447                                                    numeric,
448                                                    &owner_sid, tok+6)) {
449                                 DEBUG(5, ("Failed to parse owner sid\n"));
450                                 goto done;
451                         }
452                         have_owner = true;
453                         continue;
454                 }
455
456                 if (strncasecmp_m(tok,"OWNER+:", 7) == 0) {
457                         if (have_owner) {
458                                 DEBUG(5,("OWNER specified more than once!\n"));
459                                 goto done;
460                         }
461                         if (!convert_string_to_sid(ipc_cli, pol,
462                                                    False,
463                                                    &owner_sid, tok+7)) {
464                                 DEBUG(5, ("Failed to parse owner sid\n"));
465                                 goto done;
466                         }
467                         have_owner = true;
468                         continue;
469                 }
470
471                 if (strncasecmp_m(tok,"GROUP:", 6) == 0) {
472                         if (have_group) {
473                                 DEBUG(5,("GROUP specified more than once!\n"));
474                                 goto done;
475                         }
476                         if (!convert_string_to_sid(ipc_cli, pol,
477                                                    numeric,
478                                                    &group_sid, tok+6)) {
479                                 DEBUG(5, ("Failed to parse group sid\n"));
480                                 goto done;
481                         }
482                         have_group = true;
483                         continue;
484                 }
485
486                 if (strncasecmp_m(tok,"GROUP+:", 7) == 0) {
487                         if (have_group) {
488                                 DEBUG(5,("GROUP specified more than once!\n"));
489                                 goto done;
490                         }
491                         if (!convert_string_to_sid(ipc_cli, pol,
492                                                    False,
493                                                    &group_sid, tok+6)) {
494                                 DEBUG(5, ("Failed to parse group sid\n"));
495                                 goto done;
496                         }
497                         have_group = true;
498                         continue;
499                 }
500
501                 if (strncasecmp_m(tok,"ACL:", 4) == 0) {
502                         struct security_ace ace;
503                         if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
504                                 DEBUG(5, ("Failed to parse ACL %s\n", tok));
505                                 goto done;
506                         }
507                         if(!add_ace(&dacl, &ace, ctx)) {
508                                 DEBUG(5, ("Failed to add ACL %s\n", tok));
509                                 goto done;
510                         }
511                         continue;
512                 }
513
514                 if (strncasecmp_m(tok,"ACL+:", 5) == 0) {
515                         struct security_ace ace;
516                         if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
517                                 DEBUG(5, ("Failed to parse ACL %s\n", tok));
518                                 goto done;
519                         }
520                         if(!add_ace(&dacl, &ace, ctx)) {
521                                 DEBUG(5, ("Failed to add ACL %s\n", tok));
522                                 goto done;
523                         }
524                         continue;
525                 }
526
527                 DEBUG(5, ("Failed to parse security descriptor\n"));
528                 goto done;
529         }
530
531         ret = make_sec_desc(
532                 ctx,
533                 revision,
534                 SEC_DESC_SELF_RELATIVE,
535                 have_owner ? &owner_sid : NULL,
536                 have_group ? &group_sid : NULL,
537                 NULL,
538                 dacl,
539                 &sd_size);
540
541 done:
542         return ret;
543 }
544
545
546 /* Obtain the current dos attributes */
547 static struct DOS_ATTR_DESC *
548 dos_attr_query(SMBCCTX *context,
549                TALLOC_CTX *ctx,
550                const char *filename,
551                SMBCSRV *srv)
552 {
553         struct stat sb = {0};
554         struct DOS_ATTR_DESC *ret = NULL;
555         NTSTATUS status;
556
557         ret = talloc(ctx, struct DOS_ATTR_DESC);
558         if (!ret) {
559                 errno = ENOMEM;
560                 return NULL;
561         }
562
563         /* Obtain the DOS attributes */
564         status = SMBC_getatr(context, srv, filename, &sb);
565         if (!NT_STATUS_IS_OK(status)) {
566                 DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
567                 TALLOC_FREE(ret);
568                 errno = cli_status_to_errno(status);
569                 return NULL;
570         }
571
572         ret->mode = sb.st_mode;
573         ret->size = sb.st_size;
574         ret->create_time = sb.st_ctime;
575         ret->access_time = sb.st_atime;
576         ret->write_time = sb.st_mtime;
577         ret->change_time = sb.st_mtime;
578         ret->inode = sb.st_ino;
579
580         return ret;
581 }
582
583
584 /* parse a ascii version of a security descriptor */
585 static void
586 dos_attr_parse(SMBCCTX *context,
587                struct DOS_ATTR_DESC *dad,
588                SMBCSRV *srv,
589                char *str)
590 {
591         int n;
592         const char *p = str;
593         char *tok = NULL;
594         TALLOC_CTX *frame = NULL;
595         struct {
596                 const char * create_time_attr;
597                 const char * access_time_attr;
598                 const char * write_time_attr;
599                 const char * change_time_attr;
600         } attr_strings;
601
602         /* Determine whether to use old-style or new-style attribute names */
603         if (context->internal->full_time_names) {
604                 /* new-style names */
605                 attr_strings.create_time_attr = "CREATE_TIME";
606                 attr_strings.access_time_attr = "ACCESS_TIME";
607                 attr_strings.write_time_attr = "WRITE_TIME";
608                 attr_strings.change_time_attr = "CHANGE_TIME";
609         } else {
610                 /* old-style names */
611                 attr_strings.create_time_attr = NULL;
612                 attr_strings.access_time_attr = "A_TIME";
613                 attr_strings.write_time_attr = "M_TIME";
614                 attr_strings.change_time_attr = "C_TIME";
615         }
616
617         /* if this is to set the entire ACL... */
618         if (*str == '*') {
619                 /* ... then increment past the first colon if there is one */
620                 if ((p = strchr(str, ':')) != NULL) {
621                         ++p;
622                 } else {
623                         p = str;
624                 }
625         }
626
627         frame = talloc_stackframe();
628         while (next_token_talloc(frame, &p, &tok, "\t,\r\n")) {
629                 if (strncasecmp_m(tok, "MODE:", 5) == 0) {
630                         long request = strtol(tok+5, NULL, 16);
631                         if (request == 0) {
632                                 dad->mode =
633                                         (dad->mode & FILE_ATTRIBUTE_DIRECTORY)
634                                                 ? FILE_ATTRIBUTE_DIRECTORY
635                                                 : FILE_ATTRIBUTE_NORMAL;
636                         } else {
637                                 dad->mode = request;
638                         }
639                         continue;
640                 }
641
642                 if (strncasecmp_m(tok, "SIZE:", 5) == 0) {
643                         dad->size = (off_t)atof(tok+5);
644                         continue;
645                 }
646
647                 n = strlen(attr_strings.access_time_attr);
648                 if (strncasecmp_m(tok, attr_strings.access_time_attr, n) == 0) {
649                         dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
650                         continue;
651                 }
652
653                 n = strlen(attr_strings.change_time_attr);
654                 if (strncasecmp_m(tok, attr_strings.change_time_attr, n) == 0) {
655                         dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
656                         continue;
657                 }
658
659                 n = strlen(attr_strings.write_time_attr);
660                 if (strncasecmp_m(tok, attr_strings.write_time_attr, n) == 0) {
661                         dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
662                         continue;
663                 }
664
665                 if (attr_strings.create_time_attr != NULL) {
666                         n = strlen(attr_strings.create_time_attr);
667                         if (strncasecmp_m(tok, attr_strings.create_time_attr,
668                                         n) == 0) {
669                                 dad->create_time = (time_t)strtol(tok+n+1,
670                                                                   NULL, 10);
671                                 continue;
672                         }
673                 }
674
675                 if (strncasecmp_m(tok, "INODE:", 6) == 0) {
676                         dad->inode = (SMB_INO_T)atof(tok+6);
677                         continue;
678                 }
679         }
680         TALLOC_FREE(frame);
681 }
682
683 /*****************************************************
684  Retrieve the acls for a file.
685 *******************************************************/
686
687 static int
688 cacl_get(SMBCCTX *context,
689          TALLOC_CTX *ctx,
690          SMBCSRV *srv,
691          struct cli_state *ipc_cli,
692          struct policy_handle *pol,
693          const char *filename,
694          const char *attr_name,
695          char *buf,
696          int bufsize)
697 {
698         uint32_t i;
699         int n = 0;
700         int n_used;
701         bool all;
702         bool all_nt;
703         bool all_nt_acls;
704         bool all_dos;
705         bool some_nt;
706         bool some_dos;
707         bool exclude_nt_revision = False;
708         bool exclude_nt_owner = False;
709         bool exclude_nt_group = False;
710         bool exclude_nt_acl = False;
711         bool exclude_dos_mode = False;
712         bool exclude_dos_size = False;
713         bool exclude_dos_create_time = False;
714         bool exclude_dos_access_time = False;
715         bool exclude_dos_write_time = False;
716         bool exclude_dos_change_time = False;
717         bool exclude_dos_inode = False;
718         bool numeric = True;
719         bool determine_size = (bufsize == 0);
720         uint16_t fnum;
721         struct security_descriptor *sd;
722         fstring sidstr;
723         fstring name_sandbox;
724         char *name;
725         char *pExclude;
726         char *p;
727         struct cli_state *cli = srv->cli;
728         struct {
729                 const char * create_time_attr;
730                 const char * access_time_attr;
731                 const char * write_time_attr;
732                 const char * change_time_attr;
733         } attr_strings;
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         } excl_attr_strings;
740
741         /* Determine whether to use old-style or new-style attribute names */
742         if (context->internal->full_time_names) {
743                 /* new-style names */
744                 attr_strings.create_time_attr = "CREATE_TIME";
745                 attr_strings.access_time_attr = "ACCESS_TIME";
746                 attr_strings.write_time_attr = "WRITE_TIME";
747                 attr_strings.change_time_attr = "CHANGE_TIME";
748
749                 excl_attr_strings.create_time_attr = "CREATE_TIME";
750                 excl_attr_strings.access_time_attr = "ACCESS_TIME";
751                 excl_attr_strings.write_time_attr = "WRITE_TIME";
752                 excl_attr_strings.change_time_attr = "CHANGE_TIME";
753         } else {
754                 /* old-style names */
755                 attr_strings.create_time_attr = NULL;
756                 attr_strings.access_time_attr = "A_TIME";
757                 attr_strings.write_time_attr = "M_TIME";
758                 attr_strings.change_time_attr = "C_TIME";
759
760                 excl_attr_strings.create_time_attr = NULL;
761                 excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
762                 excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
763                 excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
764         }
765
766         /* Copy name so we can strip off exclusions (if any are specified) */
767         fstrcpy(name_sandbox, attr_name);
768
769         /* Ensure name is null terminated */
770         name_sandbox[sizeof(name_sandbox) - 1] = '\0';
771
772         /* Play in the sandbox */
773         name = name_sandbox;
774
775         /* If there are any exclusions, point to them and mask them from name */
776         if ((pExclude = strchr(name, '!')) != NULL)
777         {
778                 *pExclude++ = '\0';
779         }
780
781         all = (strncasecmp_m(name, "system.*", 8) == 0);
782         all_nt = (strncasecmp_m(name, "system.nt_sec_desc.*", 20) == 0);
783         all_nt_acls = (strncasecmp_m(name, "system.nt_sec_desc.acl.*", 24) == 0);
784         all_dos = (strncasecmp_m(name, "system.dos_attr.*", 17) == 0);
785         some_nt = (strncasecmp_m(name, "system.nt_sec_desc.", 19) == 0);
786         some_dos = (strncasecmp_m(name, "system.dos_attr.", 16) == 0);
787         numeric = (* (name + strlen(name) - 1) != '+');
788
789         /* Look for exclusions from "all" requests */
790         if (all || all_nt || all_dos) {
791                 /* Exclusions are delimited by '!' */
792                 for (;
793                      pExclude != NULL;
794                      pExclude = (p == NULL ? NULL : p + 1)) {
795
796                         /* Find end of this exclusion name */
797                         if ((p = strchr(pExclude, '!')) != NULL)
798                         {
799                                 *p = '\0';
800                         }
801
802                         /* Which exclusion name is this? */
803                         if (strcasecmp_m(pExclude,
804                                        "nt_sec_desc.revision") == 0) {
805                                 exclude_nt_revision = True;
806                         }
807                         else if (strcasecmp_m(pExclude,
808                                             "nt_sec_desc.owner") == 0) {
809                                 exclude_nt_owner = True;
810                         }
811                         else if (strcasecmp_m(pExclude,
812                                             "nt_sec_desc.group") == 0) {
813                                 exclude_nt_group = True;
814                         }
815                         else if (strcasecmp_m(pExclude,
816                                             "nt_sec_desc.acl") == 0) {
817                                 exclude_nt_acl = True;
818                         }
819                         else if (strcasecmp_m(pExclude,
820                                             "dos_attr.mode") == 0) {
821                                 exclude_dos_mode = True;
822                         }
823                         else if (strcasecmp_m(pExclude,
824                                             "dos_attr.size") == 0) {
825                                 exclude_dos_size = True;
826                         }
827                         else if (excl_attr_strings.create_time_attr != NULL &&
828                                  strcasecmp_m(pExclude,
829                                             excl_attr_strings.change_time_attr) == 0) {
830                                 exclude_dos_create_time = True;
831                         }
832                         else if (strcasecmp_m(pExclude,
833                                             excl_attr_strings.access_time_attr) == 0) {
834                                 exclude_dos_access_time = True;
835                         }
836                         else if (strcasecmp_m(pExclude,
837                                             excl_attr_strings.write_time_attr) == 0) {
838                                 exclude_dos_write_time = True;
839                         }
840                         else if (strcasecmp_m(pExclude,
841                                             excl_attr_strings.change_time_attr) == 0) {
842                                 exclude_dos_change_time = True;
843                         }
844                         else if (strcasecmp_m(pExclude, "dos_attr.inode") == 0) {
845                                 exclude_dos_inode = True;
846                         }
847                         else {
848                                 DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
849                                           pExclude));
850                                 errno = ENOATTR;
851                                 return -1;
852                         }
853                 }
854         }
855
856         n_used = 0;
857
858         /*
859          * If we are (possibly) talking to an NT or new system and some NT
860          * attributes have been requested...
861          */
862         if (ipc_cli && (all || some_nt || all_nt_acls)) {
863                 char *targetpath = NULL;
864                 struct cli_state *targetcli = NULL;
865                 struct cli_credentials *creds = NULL;
866                 NTSTATUS status;
867
868                 /* Point to the portion after "system.nt_sec_desc." */
869                 name += 19;     /* if (all) this will be invalid but unused */
870
871                 creds = context->internal->creds;
872
873                 status = cli_resolve_path(
874                         ctx, "",
875                         creds,
876                         cli, filename, &targetcli, &targetpath);
877                 if (!NT_STATUS_IS_OK(status)) {
878                         DEBUG(5, ("cacl_get Could not resolve %s\n",
879                                 filename));
880                         errno = ENOENT;
881                         return -1;
882                 }
883
884                 /* ... then obtain any NT attributes which were requested */
885                 status = cli_ntcreate(
886                         targetcli,              /* cli */
887                         targetpath,             /* fname */
888                         0,                      /* CreatFlags */
889                         READ_CONTROL_ACCESS,    /* DesiredAccess */
890                         0,                      /* FileAttributes */
891                         FILE_SHARE_READ|
892                         FILE_SHARE_WRITE,       /* ShareAccess */
893                         FILE_OPEN,              /* CreateDisposition */
894                         0x0,                    /* CreateOptions */
895                         0x0,                    /* SecurityFlags */
896                         &fnum,                  /* pfid */
897                         NULL);                  /* cr */
898                 if (!NT_STATUS_IS_OK(status)) {
899                         DEBUG(5, ("cacl_get failed to open %s: %s\n",
900                                   targetpath, nt_errstr(status)));
901                         errno = cli_status_to_errno(status);
902                         return -1;
903                 }
904
905                 status = cli_query_secdesc(targetcli, fnum, ctx, &sd);
906                 if (!NT_STATUS_IS_OK(status)) {
907                         DEBUG(5,("cacl_get Failed to query old descriptor "
908                                  "of %s: %s\n",
909                                   targetpath, nt_errstr(status)));
910                         errno = cli_status_to_errno(status);
911                         return -1;
912                 }
913
914                 cli_close(targetcli, fnum);
915
916                 if (! exclude_nt_revision) {
917                         if (all || all_nt) {
918                                 if (determine_size) {
919                                         p = talloc_asprintf(ctx,
920                                                             "REVISION:%d",
921                                                             sd->revision);
922                                         if (!p) {
923                                                 errno = ENOMEM;
924                                                 return -1;
925                                         }
926                                         n = strlen(p);
927                                 } else {
928                                         n = snprintf(buf, bufsize,
929                                                      "REVISION:%d",
930                                                      sd->revision);
931                                 }
932                         } else if (strcasecmp_m(name, "revision") == 0) {
933                                 if (determine_size) {
934                                         p = talloc_asprintf(ctx, "%d",
935                                                             sd->revision);
936                                         if (!p) {
937                                                 errno = ENOMEM;
938                                                 return -1;
939                                         }
940                                         n = strlen(p);
941                                 } else {
942                                         n = snprintf(buf, bufsize, "%d",
943                                                      sd->revision);
944                                 }
945                         }
946
947                         if (!determine_size && n > bufsize) {
948                                 errno = ERANGE;
949                                 return -1;
950                         }
951                         buf += n;
952                         n_used += n;
953                         bufsize -= n;
954                         n = 0;
955                 }
956
957                 if (! exclude_nt_owner) {
958                         /* Get owner and group sid */
959                         if (sd->owner_sid) {
960                                 convert_sid_to_string(ipc_cli, pol,
961                                                       sidstr,
962                                                       numeric,
963                                                       sd->owner_sid);
964                         } else {
965                                 fstrcpy(sidstr, "");
966                         }
967
968                         if (all || all_nt) {
969                                 if (determine_size) {
970                                         p = talloc_asprintf(ctx, ",OWNER:%s",
971                                                             sidstr);
972                                         if (!p) {
973                                                 errno = ENOMEM;
974                                                 return -1;
975                                         }
976                                         n = strlen(p);
977                                 } else if (sidstr[0] != '\0') {
978                                         n = snprintf(buf, bufsize,
979                                                      ",OWNER:%s", sidstr);
980                                 }
981                         } else if (strncasecmp_m(name, "owner", 5) == 0) {
982                                 if (determine_size) {
983                                         p = talloc_asprintf(ctx, "%s", sidstr);
984                                         if (!p) {
985                                                 errno = ENOMEM;
986                                                 return -1;
987                                         }
988                                         n = strlen(p);
989                                 } else {
990                                         n = snprintf(buf, bufsize, "%s",
991                                                      sidstr);
992                                 }
993                         }
994
995                         if (!determine_size && n > bufsize) {
996                                 errno = ERANGE;
997                                 return -1;
998                         }
999                         buf += n;
1000                         n_used += n;
1001                         bufsize -= n;
1002                         n = 0;
1003                 }
1004
1005                 if (! exclude_nt_group) {
1006                         if (sd->group_sid) {
1007                                 convert_sid_to_string(ipc_cli, pol,
1008                                                       sidstr, numeric,
1009                                                       sd->group_sid);
1010                         } else {
1011                                 fstrcpy(sidstr, "");
1012                         }
1013
1014                         if (all || all_nt) {
1015                                 if (determine_size) {
1016                                         p = talloc_asprintf(ctx, ",GROUP:%s",
1017                                                             sidstr);
1018                                         if (!p) {
1019                                                 errno = ENOMEM;
1020                                                 return -1;
1021                                         }
1022                                         n = strlen(p);
1023                                 } else if (sidstr[0] != '\0') {
1024                                         n = snprintf(buf, bufsize,
1025                                                      ",GROUP:%s", sidstr);
1026                                 }
1027                         } else if (strncasecmp_m(name, "group", 5) == 0) {
1028                                 if (determine_size) {
1029                                         p = talloc_asprintf(ctx, "%s", sidstr);
1030                                         if (!p) {
1031                                                 errno = ENOMEM;
1032                                                 return -1;
1033                                         }
1034                                         n = strlen(p);
1035                                 } else {
1036                                         n = snprintf(buf, bufsize,
1037                                                      "%s", sidstr);
1038                                 }
1039                         }
1040
1041                         if (!determine_size && n > bufsize) {
1042                                 errno = ERANGE;
1043                                 return -1;
1044                         }
1045                         buf += n;
1046                         n_used += n;
1047                         bufsize -= n;
1048                         n = 0;
1049                 }
1050
1051                 if (! exclude_nt_acl) {
1052                         /* Add aces to value buffer  */
1053                         for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
1054
1055                                 struct security_ace *ace = &sd->dacl->aces[i];
1056                                 convert_sid_to_string(ipc_cli, pol,
1057                                                       sidstr, numeric,
1058                                                       &ace->trustee);
1059
1060                                 if (all || all_nt) {
1061                                         if (determine_size) {
1062                                                 p = talloc_asprintf(
1063                                                         ctx,
1064                                                         ",ACL:"
1065                                                         "%s:%d/%d/0x%08x",
1066                                                         sidstr,
1067                                                         ace->type,
1068                                                         ace->flags,
1069                                                         ace->access_mask);
1070                                                 if (!p) {
1071                                                         errno = ENOMEM;
1072                                                         return -1;
1073                                                 }
1074                                                 n = strlen(p);
1075                                         } else {
1076                                                 n = snprintf(
1077                                                         buf, bufsize,
1078                                                         ",ACL:%s:%d/%d/0x%08x",
1079                                                         sidstr,
1080                                                         ace->type,
1081                                                         ace->flags,
1082                                                         ace->access_mask);
1083                                         }
1084                                 } else if ((strncasecmp_m(name, "acl", 3) == 0 &&
1085                                             strcasecmp_m(name+3, sidstr) == 0) ||
1086                                            (strncasecmp_m(name, "acl+", 4) == 0 &&
1087                                             strcasecmp_m(name+4, sidstr) == 0)) {
1088                                         if (determine_size) {
1089                                                 p = talloc_asprintf(
1090                                                         ctx,
1091                                                         "%d/%d/0x%08x",
1092                                                         ace->type,
1093                                                         ace->flags,
1094                                                         ace->access_mask);
1095                                                 if (!p) {
1096                                                         errno = ENOMEM;
1097                                                         return -1;
1098                                                 }
1099                                                 n = strlen(p);
1100                                         } else {
1101                                                 n = snprintf(buf, bufsize,
1102                                                              "%d/%d/0x%08x",
1103                                                              ace->type,
1104                                                              ace->flags,
1105                                                              ace->access_mask);
1106                                         }
1107                                 } else if (all_nt_acls) {
1108                                         if (determine_size) {
1109                                                 p = talloc_asprintf(
1110                                                         ctx,
1111                                                         "%s%s:%d/%d/0x%08x",
1112                                                         i ? "," : "",
1113                                                         sidstr,
1114                                                         ace->type,
1115                                                         ace->flags,
1116                                                         ace->access_mask);
1117                                                 if (!p) {
1118                                                         errno = ENOMEM;
1119                                                         return -1;
1120                                                 }
1121                                                 n = strlen(p);
1122                                         } else {
1123                                                 n = snprintf(buf, bufsize,
1124                                                              "%s%s:%d/%d/0x%08x",
1125                                                              i ? "," : "",
1126                                                              sidstr,
1127                                                              ace->type,
1128                                                              ace->flags,
1129                                                              ace->access_mask);
1130                                         }
1131                                 }
1132                                 if (!determine_size && n > bufsize) {
1133                                         errno = ERANGE;
1134                                         return -1;
1135                                 }
1136                                 buf += n;
1137                                 n_used += n;
1138                                 bufsize -= n;
1139                                 n = 0;
1140                         }
1141                 }
1142
1143                 /* Restore name pointer to its original value */
1144                 name -= 19;
1145         }
1146
1147         if (all || some_dos) {
1148                 struct stat sb = {0};
1149                 time_t create_time = (time_t)0;
1150                 time_t write_time = (time_t)0;
1151                 time_t access_time = (time_t)0;
1152                 time_t change_time = (time_t)0;
1153                 off_t size = 0;
1154                 uint16_t mode = 0;
1155                 SMB_INO_T ino = 0;
1156                 NTSTATUS status;
1157
1158                 /* Point to the portion after "system.dos_attr." */
1159                 name += 16;     /* if (all) this will be invalid but unused */
1160
1161                 /* Obtain the DOS attributes */
1162                 status = SMBC_getatr(context, srv, filename, &sb);
1163                 if (!NT_STATUS_IS_OK(status)) {
1164                         errno = cli_status_to_errno(status);
1165                         return -1;
1166                 }
1167
1168                 create_time = sb.st_ctime;
1169                 access_time = sb.st_atime;
1170                 write_time  = sb.st_mtime;
1171                 change_time = sb.st_mtime;
1172                 size        = sb.st_size;
1173                 mode        = sb.st_mode;
1174                 ino         = sb.st_ino;
1175
1176                 if (! exclude_dos_mode) {
1177                         if (all || all_dos) {
1178                                 if (determine_size) {
1179                                         p = talloc_asprintf(ctx,
1180                                                             "%sMODE:0x%x",
1181                                                             (ipc_cli &&
1182                                                              (all || some_nt)
1183                                                              ? ","
1184                                                              : ""),
1185                                                             mode);
1186                                         if (!p) {
1187                                                 errno = ENOMEM;
1188                                                 return -1;
1189                                         }
1190                                         n = strlen(p);
1191                                 } else {
1192                                         n = snprintf(buf, bufsize,
1193                                                      "%sMODE:0x%x",
1194                                                      (ipc_cli &&
1195                                                       (all || some_nt)
1196                                                       ? ","
1197                                                       : ""),
1198                                                      mode);
1199                                 }
1200                         } else if (strcasecmp_m(name, "mode") == 0) {
1201                                 if (determine_size) {
1202                                         p = talloc_asprintf(ctx, "0x%x", mode);
1203                                         if (!p) {
1204                                                 errno = ENOMEM;
1205                                                 return -1;
1206                                         }
1207                                         n = strlen(p);
1208                                 } else {
1209                                         n = snprintf(buf, bufsize,
1210                                                      "0x%x", mode);
1211                                 }
1212                         }
1213
1214                         if (!determine_size && n > bufsize) {
1215                                 errno = ERANGE;
1216                                 return -1;
1217                         }
1218                         buf += n;
1219                         n_used += n;
1220                         bufsize -= n;
1221                         n = 0;
1222                 }
1223
1224                 if (! exclude_dos_size) {
1225                         if (all || all_dos) {
1226                                 if (determine_size) {
1227                                         p = talloc_asprintf(
1228                                                 ctx,
1229                                                 ",SIZE:%.0f",
1230                                                 (double)size);
1231                                         if (!p) {
1232                                                 errno = ENOMEM;
1233                                                 return -1;
1234                                         }
1235                                         n = strlen(p);
1236                                 } else {
1237                                         n = snprintf(buf, bufsize,
1238                                                      ",SIZE:%.0f",
1239                                                      (double)size);
1240                                 }
1241                         } else if (strcasecmp_m(name, "size") == 0) {
1242                                 if (determine_size) {
1243                                         p = talloc_asprintf(
1244                                                 ctx,
1245                                                 "%.0f",
1246                                                 (double)size);
1247                                         if (!p) {
1248                                                 errno = ENOMEM;
1249                                                 return -1;
1250                                         }
1251                                         n = strlen(p);
1252                                 } else {
1253                                         n = snprintf(buf, bufsize,
1254                                                      "%.0f",
1255                                                      (double)size);
1256                                 }
1257                         }
1258
1259                         if (!determine_size && n > bufsize) {
1260                                 errno = ERANGE;
1261                                 return -1;
1262                         }
1263                         buf += n;
1264                         n_used += n;
1265                         bufsize -= n;
1266                         n = 0;
1267                 }
1268
1269                 if (! exclude_dos_create_time &&
1270                     attr_strings.create_time_attr != NULL) {
1271                         if (all || all_dos) {
1272                                 if (determine_size) {
1273                                         p = talloc_asprintf(ctx,
1274                                                             ",%s:%lu",
1275                                                             attr_strings.create_time_attr,
1276                                                             (unsigned long) create_time);
1277                                         if (!p) {
1278                                                 errno = ENOMEM;
1279                                                 return -1;
1280                                         }
1281                                         n = strlen(p);
1282                                 } else {
1283                                         n = snprintf(buf, bufsize,
1284                                                      ",%s:%lu",
1285                                                      attr_strings.create_time_attr,
1286                                                      (unsigned long) create_time);
1287                                 }
1288                         } else if (strcasecmp_m(name, attr_strings.create_time_attr) == 0) {
1289                                 if (determine_size) {
1290                                         p = talloc_asprintf(ctx, "%lu", (unsigned long) create_time);
1291                                         if (!p) {
1292                                                 errno = ENOMEM;
1293                                                 return -1;
1294                                         }
1295                                         n = strlen(p);
1296                                 } else {
1297                                         n = snprintf(buf, bufsize,
1298                                                      "%lu", (unsigned long) create_time);
1299                                 }
1300                         }
1301
1302                         if (!determine_size && n > bufsize) {
1303                                 errno = ERANGE;
1304                                 return -1;
1305                         }
1306                         buf += n;
1307                         n_used += n;
1308                         bufsize -= n;
1309                         n = 0;
1310                 }
1311
1312                 if (! exclude_dos_access_time) {
1313                         if (all || all_dos) {
1314                                 if (determine_size) {
1315                                         p = talloc_asprintf(ctx,
1316                                                             ",%s:%lu",
1317                                                             attr_strings.access_time_attr,
1318                                                             (unsigned long) access_time);
1319                                         if (!p) {
1320                                                 errno = ENOMEM;
1321                                                 return -1;
1322                                         }
1323                                         n = strlen(p);
1324                                 } else {
1325                                         n = snprintf(buf, bufsize,
1326                                                      ",%s:%lu",
1327                                                      attr_strings.access_time_attr,
1328                                                      (unsigned long) access_time);
1329                                 }
1330                         } else if (strcasecmp_m(name, attr_strings.access_time_attr) == 0) {
1331                                 if (determine_size) {
1332                                         p = talloc_asprintf(ctx, "%lu", (unsigned long) access_time);
1333                                         if (!p) {
1334                                                 errno = ENOMEM;
1335                                                 return -1;
1336                                         }
1337                                         n = strlen(p);
1338                                 } else {
1339                                         n = snprintf(buf, bufsize,
1340                                                      "%lu", (unsigned long) access_time);
1341                                 }
1342                         }
1343
1344                         if (!determine_size && n > bufsize) {
1345                                 errno = ERANGE;
1346                                 return -1;
1347                         }
1348                         buf += n;
1349                         n_used += n;
1350                         bufsize -= n;
1351                         n = 0;
1352                 }
1353
1354                 if (! exclude_dos_write_time) {
1355                         if (all || all_dos) {
1356                                 if (determine_size) {
1357                                         p = talloc_asprintf(ctx,
1358                                                             ",%s:%lu",
1359                                                             attr_strings.write_time_attr,
1360                                                             (unsigned long) write_time);
1361                                         if (!p) {
1362                                                 errno = ENOMEM;
1363                                                 return -1;
1364                                         }
1365                                         n = strlen(p);
1366                                 } else {
1367                                         n = snprintf(buf, bufsize,
1368                                                      ",%s:%lu",
1369                                                      attr_strings.write_time_attr,
1370                                                      (unsigned long) write_time);
1371                                 }
1372                         } else if (strcasecmp_m(name, attr_strings.write_time_attr) == 0) {
1373                                 if (determine_size) {
1374                                         p = talloc_asprintf(ctx, "%lu", (unsigned long) write_time);
1375                                         if (!p) {
1376                                                 errno = ENOMEM;
1377                                                 return -1;
1378                                         }
1379                                         n = strlen(p);
1380                                 } else {
1381                                         n = snprintf(buf, bufsize,
1382                                                      "%lu", (unsigned long) write_time);
1383                                 }
1384                         }
1385
1386                         if (!determine_size && n > bufsize) {
1387                                 errno = ERANGE;
1388                                 return -1;
1389                         }
1390                         buf += n;
1391                         n_used += n;
1392                         bufsize -= n;
1393                         n = 0;
1394                 }
1395
1396                 if (! exclude_dos_change_time) {
1397                         if (all || all_dos) {
1398                                 if (determine_size) {
1399                                         p = talloc_asprintf(ctx,
1400                                                             ",%s:%lu",
1401                                                             attr_strings.change_time_attr,
1402                                                             (unsigned long) change_time);
1403                                         if (!p) {
1404                                                 errno = ENOMEM;
1405                                                 return -1;
1406                                         }
1407                                         n = strlen(p);
1408                                 } else {
1409                                         n = snprintf(buf, bufsize,
1410                                                      ",%s:%lu",
1411                                                      attr_strings.change_time_attr,
1412                                                      (unsigned long) change_time);
1413                                 }
1414                         } else if (strcasecmp_m(name, attr_strings.change_time_attr) == 0) {
1415                                 if (determine_size) {
1416                                         p = talloc_asprintf(ctx, "%lu", (unsigned long) change_time);
1417                                         if (!p) {
1418                                                 errno = ENOMEM;
1419                                                 return -1;
1420                                         }
1421                                         n = strlen(p);
1422                                 } else {
1423                                         n = snprintf(buf, bufsize,
1424                                                      "%lu", (unsigned long) change_time);
1425                                 }
1426                         }
1427
1428                         if (!determine_size && n > bufsize) {
1429                                 errno = ERANGE;
1430                                 return -1;
1431                         }
1432                         buf += n;
1433                         n_used += n;
1434                         bufsize -= n;
1435                         n = 0;
1436                 }
1437
1438                 if (! exclude_dos_inode) {
1439                         if (all || all_dos) {
1440                                 if (determine_size) {
1441                                         p = talloc_asprintf(
1442                                                 ctx,
1443                                                 ",INODE:%.0f",
1444                                                 (double)ino);
1445                                         if (!p) {
1446                                                 errno = ENOMEM;
1447                                                 return -1;
1448                                         }
1449                                         n = strlen(p);
1450                                 } else {
1451                                         n = snprintf(buf, bufsize,
1452                                                      ",INODE:%.0f",
1453                                                      (double) ino);
1454                                 }
1455                         } else if (strcasecmp_m(name, "inode") == 0) {
1456                                 if (determine_size) {
1457                                         p = talloc_asprintf(
1458                                                 ctx,
1459                                                 "%.0f",
1460                                                 (double) ino);
1461                                         if (!p) {
1462                                                 errno = ENOMEM;
1463                                                 return -1;
1464                                         }
1465                                         n = strlen(p);
1466                                 } else {
1467                                         n = snprintf(buf, bufsize,
1468                                                      "%.0f",
1469                                                      (double) ino);
1470                                 }
1471                         }
1472
1473                         if (!determine_size && n > bufsize) {
1474                                 errno = ERANGE;
1475                                 return -1;
1476                         }
1477                         buf += n;
1478                         n_used += n;
1479                         bufsize -= n;
1480                         n = 0;
1481                 }
1482
1483                 /* Restore name pointer to its original value */
1484                 name -= 16;
1485         }
1486
1487         if (n_used == 0) {
1488                 errno = ENOATTR;
1489                 return -1;
1490         }
1491
1492         return n_used;
1493 }
1494
1495 /*****************************************************
1496 set the ACLs on a file given an ascii description
1497 *******************************************************/
1498 static int
1499 cacl_set(SMBCCTX *context,
1500         TALLOC_CTX *ctx,
1501         struct cli_state *cli,
1502         struct cli_state *ipc_cli,
1503         struct policy_handle *pol,
1504         const char *filename,
1505         char *the_acl,
1506         int mode,
1507         int flags)
1508 {
1509         uint16_t fnum = (uint16_t)-1;
1510         int err = 0;
1511         struct security_descriptor *sd = NULL, *old;
1512         struct security_acl *dacl = NULL;
1513         struct dom_sid *owner_sid = NULL;
1514         struct dom_sid *group_sid = NULL;
1515         uint32_t i, j;
1516         size_t sd_size;
1517         int ret = 0;
1518         char *p;
1519         bool numeric = True;
1520         char *targetpath = NULL;
1521         struct cli_state *targetcli = NULL;
1522         struct cli_credentials *creds = NULL;
1523         NTSTATUS status;
1524
1525         /* the_acl will be null for REMOVE_ALL operations */
1526         if (the_acl) {
1527                 numeric = ((p = strchr(the_acl, ':')) != NULL &&
1528                            p > the_acl &&
1529                            p[-1] != '+');
1530
1531                 /* if this is to set the entire ACL... */
1532                 if (*the_acl == '*') {
1533                         /* ... then increment past the first colon */
1534                         the_acl = p + 1;
1535                 }
1536
1537                 sd = sec_desc_parse(ctx, ipc_cli, pol, numeric, the_acl);
1538                 if (!sd) {
1539                         errno = EINVAL;
1540                         return -1;
1541                 }
1542         }
1543
1544         /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
1545            that doesn't deref sd */
1546
1547         if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
1548                 errno = EINVAL;
1549                 return -1;
1550         }
1551
1552         creds = context->internal->creds;
1553
1554         status = cli_resolve_path(ctx, "",
1555                                   creds,
1556                                   cli, filename, &targetcli, &targetpath);
1557         if (!NT_STATUS_IS_OK(status)) {
1558                 DEBUG(5,("cacl_set: Could not resolve %s\n", filename));
1559                 errno = ENOENT;
1560                 return -1;
1561         }
1562
1563         /* The desired access below is the only one I could find that works
1564            with NT4, W2KP and Samba */
1565
1566         status = cli_ntcreate(
1567                 targetcli,              /* cli */
1568                 targetpath,             /* fname */
1569                 0,                      /* CreatFlags */
1570                 READ_CONTROL_ACCESS,    /* DesiredAccess */
1571                 0,                      /* FileAttributes */
1572                 FILE_SHARE_READ|
1573                 FILE_SHARE_WRITE,       /* ShareAccess */
1574                 FILE_OPEN,              /* CreateDisposition */
1575                 0x0,                    /* CreateOptions */
1576                 0x0,                    /* SecurityFlags */
1577                 &fnum,                  /* pfid */
1578                 NULL);                  /* cr */
1579         if (!NT_STATUS_IS_OK(status)) {
1580                 DEBUG(5, ("cacl_set failed to open %s: %s\n",
1581                           targetpath, nt_errstr(status)));
1582                 errno = 0;
1583                 return -1;
1584         }
1585
1586         status = cli_query_secdesc(targetcli, fnum, ctx, &old);
1587         if (!NT_STATUS_IS_OK(status)) {
1588                 DEBUG(5,("cacl_set Failed to query old descriptor of %s: %s\n",
1589                          targetpath, nt_errstr(status)));
1590                 errno = 0;
1591                 return -1;
1592         }
1593
1594         cli_close(targetcli, fnum);
1595
1596         switch (mode) {
1597         case SMBC_XATTR_MODE_REMOVE_ALL:
1598                 old->dacl->num_aces = 0;
1599                 dacl = old->dacl;
1600                 break;
1601
1602         case SMBC_XATTR_MODE_REMOVE:
1603                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
1604                         bool found = False;
1605
1606                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
1607                                 if (security_ace_equal(&sd->dacl->aces[i],
1608                                                        &old->dacl->aces[j])) {
1609                                         uint32_t k;
1610                                         for (k=j; k<old->dacl->num_aces-1;k++) {
1611                                                 old->dacl->aces[k] =
1612                                                         old->dacl->aces[k+1];
1613                                         }
1614                                         old->dacl->num_aces--;
1615                                         found = True;
1616                                         dacl = old->dacl;
1617                                         break;
1618                                 }
1619                         }
1620
1621                         if (!found) {
1622                                 err = ENOATTR;
1623                                 ret = -1;
1624                                 goto failed;
1625                         }
1626                 }
1627                 break;
1628
1629         case SMBC_XATTR_MODE_ADD:
1630                 for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
1631                         bool found = False;
1632
1633                         for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
1634                                 if (dom_sid_equal(&sd->dacl->aces[i].trustee,
1635                                               &old->dacl->aces[j].trustee)) {
1636                                         if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
1637                                                 err = EEXIST;
1638                                                 ret = -1;
1639                                                 goto failed;
1640                                         }
1641                                         old->dacl->aces[j] = sd->dacl->aces[i];
1642                                         ret = -1;
1643                                         found = True;
1644                                 }
1645                         }
1646
1647                         if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
1648                                 err = ENOATTR;
1649                                 ret = -1;
1650                                 goto failed;
1651                         }
1652
1653                         for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
1654                                 add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
1655                         }
1656                 }
1657                 dacl = old->dacl;
1658                 break;
1659
1660         case SMBC_XATTR_MODE_SET:
1661                 old = sd;
1662                 owner_sid = old->owner_sid;
1663                 group_sid = old->group_sid;
1664                 dacl = old->dacl;
1665                 break;
1666
1667         case SMBC_XATTR_MODE_CHOWN:
1668                 owner_sid = sd->owner_sid;
1669                 break;
1670
1671         case SMBC_XATTR_MODE_CHGRP:
1672                 group_sid = sd->group_sid;
1673                 break;
1674         }
1675
1676         /* Denied ACE entries must come before allowed ones */
1677         sort_acl(old->dacl);
1678
1679         /* Create new security descriptor and set it */
1680         sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
1681                            owner_sid, group_sid, NULL, dacl, &sd_size);
1682
1683         status = cli_ntcreate(targetcli, targetpath, 0,
1684                               WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS, 0,
1685                               FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
1686                               0x0, 0x0, &fnum, NULL);
1687         if (!NT_STATUS_IS_OK(status)) {
1688                 DEBUG(5, ("cacl_set failed to open %s: %s\n",
1689                           targetpath, nt_errstr(status)));
1690                 errno = 0;
1691                 return -1;
1692         }
1693
1694         status = cli_set_secdesc(targetcli, fnum, sd);
1695         if (!NT_STATUS_IS_OK(status)) {
1696                 DEBUG(5, ("ERROR: secdesc set failed: %s\n",
1697                           nt_errstr(status)));
1698                 ret = -1;
1699         }
1700
1701         /* Clean up */
1702
1703 failed:
1704         cli_close(targetcli, fnum);
1705
1706         if (err != 0) {
1707                 errno = err;
1708         }
1709
1710         return ret;
1711 }
1712
1713
1714 int
1715 SMBC_setxattr_ctx(SMBCCTX *context,
1716                   const char *fname,
1717                   const char *name,
1718                   const void *value,
1719                   size_t size,
1720                   int flags)
1721 {
1722         int ret;
1723         int ret2;
1724         SMBCSRV *srv = NULL;
1725         SMBCSRV *ipc_srv = NULL;
1726         char *server = NULL;
1727         char *share = NULL;
1728         char *user = NULL;
1729         char *password = NULL;
1730         char *workgroup = NULL;
1731         char *path = NULL;
1732         struct DOS_ATTR_DESC *dad = NULL;
1733         struct {
1734                 const char * create_time_attr;
1735                 const char * access_time_attr;
1736                 const char * write_time_attr;
1737                 const char * change_time_attr;
1738         } attr_strings;
1739         uint16_t port = 0;
1740         TALLOC_CTX *frame = talloc_stackframe();
1741
1742         if (!context || !context->internal->initialized) {
1743                 errno = EINVAL;  /* Best I can think of ... */
1744                 TALLOC_FREE(frame);
1745                 return -1;
1746         }
1747
1748         if (!fname) {
1749                 errno = EINVAL;
1750                 TALLOC_FREE(frame);
1751                 return -1;
1752         }
1753
1754         DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
1755                   fname, name, (int) size, (const char*)value));
1756
1757         if (SMBC_parse_path(frame,
1758                             context,
1759                             fname,
1760                             &workgroup,
1761                             &server,
1762                             &port,
1763                             &share,
1764                             &path,
1765                             &user,
1766                             &password,
1767                             NULL)) {
1768                 errno = EINVAL;
1769                 TALLOC_FREE(frame);
1770                 return -1;
1771         }
1772
1773         if (!user || user[0] == (char)0) {
1774                 user = talloc_strdup(frame, smbc_getUser(context));
1775                 if (!user) {
1776                         errno = ENOMEM;
1777                         TALLOC_FREE(frame);
1778                         return -1;
1779                 }
1780         }
1781
1782         srv = SMBC_server(frame, context, True,
1783                           server, port, share, &workgroup, &user, &password);
1784         if (!srv) {
1785                 TALLOC_FREE(frame);
1786                 return -1;  /* errno set by SMBC_server */
1787         }
1788
1789         if (! srv->no_nt_session) {
1790                 ipc_srv = SMBC_attr_server(frame, context, server, port, share,
1791                                            &workgroup, &user, &password);
1792                 if (! ipc_srv) {
1793                         srv->no_nt_session = True;
1794                 }
1795         } else {
1796                 ipc_srv = NULL;
1797         }
1798
1799         /*
1800          * Are they asking to set the entire set of known attributes?
1801          */
1802         if (strcasecmp_m(name, "system.*") == 0 ||
1803             strcasecmp_m(name, "system.*+") == 0) {
1804                 /* Yup. */
1805                 char *namevalue =
1806                         talloc_asprintf(talloc_tos(), "%s:%s",
1807                                         name+7, (const char *) value);
1808                 if (! namevalue) {
1809                         errno = ENOMEM;
1810                         ret = -1;
1811                         TALLOC_FREE(frame);
1812                         return -1;
1813                 }
1814
1815                 if (ipc_srv) {
1816                         ret = cacl_set(context, talloc_tos(), srv->cli,
1817                                        ipc_srv->cli, &ipc_srv->pol, path,
1818                                        namevalue,
1819                                        (*namevalue == '*'
1820                                         ? SMBC_XATTR_MODE_SET
1821                                         : SMBC_XATTR_MODE_ADD),
1822                                        flags);
1823                 } else {
1824                         ret = 0;
1825                 }
1826
1827                 /* get a DOS Attribute Descriptor with current attributes */
1828                 dad = dos_attr_query(context, talloc_tos(), path, srv);
1829                 if (dad) {
1830                         bool ok;
1831
1832                         /* Overwrite old with new, using what was provided */
1833                         dos_attr_parse(context, dad, srv, namevalue);
1834
1835                         /* Set the new DOS attributes */
1836                         ok = SMBC_setatr(
1837                                 context,
1838                                 srv,
1839                                 path,
1840                                 (struct timespec) {
1841                                         .tv_sec = dad->create_time },
1842                                 (struct timespec) {
1843                                         .tv_sec = dad->access_time },
1844                                 (struct timespec) {
1845                                         .tv_sec = dad->write_time },
1846                                 (struct timespec) {
1847                                         .tv_sec = dad->change_time },
1848                                 dad->mode);
1849                         if (!ok) {
1850                                 /* cause failure if NT failed too */
1851                                 dad = NULL;
1852                         }
1853                 }
1854
1855                 /* we only fail if both NT and DOS sets failed */
1856                 if (ret < 0 && ! dad) {
1857                         ret = -1; /* in case dad was null */
1858                 }
1859                 else {
1860                         ret = 0;
1861                 }
1862
1863                 TALLOC_FREE(frame);
1864                 return ret;
1865         }
1866
1867         /*
1868          * Are they asking to set an access control element or to set
1869          * the entire access control list?
1870          */
1871         if (strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
1872             strcasecmp_m(name, "system.nt_sec_desc.*+") == 0 ||
1873             strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
1874             strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
1875             strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0) {
1876
1877                 /* Yup. */
1878                 char *namevalue =
1879                         talloc_asprintf(talloc_tos(), "%s:%s",
1880                                         name+19, (const char *) value);
1881
1882                 if (! ipc_srv) {
1883                         ret = -1; /* errno set by SMBC_server() */
1884                 }
1885                 else if (! namevalue) {
1886                         errno = ENOMEM;
1887                         ret = -1;
1888                 } else {
1889                         ret = cacl_set(context, talloc_tos(), srv->cli,
1890                                        ipc_srv->cli, &ipc_srv->pol, path,
1891                                        namevalue,
1892                                        (*namevalue == '*'
1893                                         ? SMBC_XATTR_MODE_SET
1894                                         : SMBC_XATTR_MODE_ADD),
1895                                        flags);
1896                 }
1897                 TALLOC_FREE(frame);
1898                 return ret;
1899         }
1900
1901         /*
1902          * Are they asking to set the owner?
1903          */
1904         if (strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
1905             strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0) {
1906
1907                 /* Yup. */
1908                 char *namevalue =
1909                         talloc_asprintf(talloc_tos(), "%s:%s",
1910                                         name+19, (const char *) value);
1911
1912                 if (! ipc_srv) {
1913                         ret = -1; /* errno set by SMBC_server() */
1914                 }
1915                 else if (! namevalue) {
1916                         errno = ENOMEM;
1917                         ret = -1;
1918                 } else {
1919                         ret = cacl_set(context, talloc_tos(), srv->cli,
1920                                        ipc_srv->cli, &ipc_srv->pol, path,
1921                                        namevalue, SMBC_XATTR_MODE_CHOWN, 0);
1922                 }
1923                 TALLOC_FREE(frame);
1924                 return ret;
1925         }
1926
1927         /*
1928          * Are they asking to set the group?
1929          */
1930         if (strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
1931             strcasecmp_m(name, "system.nt_sec_desc.group+") == 0) {
1932
1933                 /* Yup. */
1934                 char *namevalue =
1935                         talloc_asprintf(talloc_tos(), "%s:%s",
1936                                         name+19, (const char *) value);
1937
1938                 if (! ipc_srv) {
1939                         /* errno set by SMBC_server() */
1940                         ret = -1;
1941                 }
1942                 else if (! namevalue) {
1943                         errno = ENOMEM;
1944                         ret = -1;
1945                 } else {
1946                         ret = cacl_set(context, talloc_tos(), srv->cli,
1947                                        ipc_srv->cli, &ipc_srv->pol, path,
1948                                        namevalue, SMBC_XATTR_MODE_CHGRP, 0);
1949                 }
1950                 TALLOC_FREE(frame);
1951                 return ret;
1952         }
1953
1954         /* Determine whether to use old-style or new-style attribute names */
1955         if (context->internal->full_time_names) {
1956                 /* new-style names */
1957                 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
1958                 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
1959                 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
1960                 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
1961         } else {
1962                 /* old-style names */
1963                 attr_strings.create_time_attr = NULL;
1964                 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
1965                 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
1966                 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
1967         }
1968
1969         /*
1970          * Are they asking to set a DOS attribute?
1971          */
1972         if (strcasecmp_m(name, "system.dos_attr.*") == 0 ||
1973             strcasecmp_m(name, "system.dos_attr.mode") == 0 ||
1974             (attr_strings.create_time_attr != NULL &&
1975              strcasecmp_m(name, attr_strings.create_time_attr) == 0) ||
1976             strcasecmp_m(name, attr_strings.access_time_attr) == 0 ||
1977             strcasecmp_m(name, attr_strings.write_time_attr) == 0 ||
1978             strcasecmp_m(name, attr_strings.change_time_attr) == 0) {
1979
1980                 /* get a DOS Attribute Descriptor with current attributes */
1981                 dad = dos_attr_query(context, talloc_tos(), path, srv);
1982                 if (dad) {
1983                         char *namevalue =
1984                                 talloc_asprintf(talloc_tos(), "%s:%s",
1985                                                 name+16, (const char *) value);
1986                         if (! namevalue) {
1987                                 errno = ENOMEM;
1988                                 ret = -1;
1989                         } else {
1990                                 /* Overwrite old with provided new params */
1991                                 dos_attr_parse(context, dad, srv, namevalue);
1992
1993                                 /* Set the new DOS attributes */
1994                                 ret2 = SMBC_setatr(
1995                                         context,
1996                                         srv,
1997                                         path,
1998                                         (struct timespec) {
1999                                                 .tv_sec = dad->create_time },
2000                                         (struct timespec) {
2001                                                 .tv_sec = dad->access_time },
2002                                         (struct timespec) {
2003                                                 .tv_sec = dad->write_time },
2004                                         (struct timespec) {
2005                                                 .tv_sec = dad->change_time },
2006                                         dad->mode);
2007
2008                                 /* ret2 has True (success) / False (failure) */
2009                                 if (ret2) {
2010                                         ret = 0;
2011                                 } else {
2012                                         ret = -1;
2013                                 }
2014                         }
2015                 } else {
2016                         ret = -1;
2017                 }
2018
2019                 TALLOC_FREE(frame);
2020                 return ret;
2021         }
2022
2023         /* Unsupported attribute name */
2024         errno = EINVAL;
2025         TALLOC_FREE(frame);
2026         return -1;
2027 }
2028
2029 int
2030 SMBC_getxattr_ctx(SMBCCTX *context,
2031                   const char *fname,
2032                   const char *name,
2033                   const void *value,
2034                   size_t size)
2035 {
2036         int ret;
2037         SMBCSRV *srv = NULL;
2038         SMBCSRV *ipc_srv = NULL;
2039         char *server = NULL;
2040         char *share = NULL;
2041         char *user = NULL;
2042         char *password = NULL;
2043         char *workgroup = NULL;
2044         char *path = NULL;
2045         struct {
2046                 const char * create_time_attr;
2047                 const char * access_time_attr;
2048                 const char * write_time_attr;
2049                 const char * change_time_attr;
2050         } attr_strings;
2051         uint16_t port = 0;
2052         TALLOC_CTX *frame = talloc_stackframe();
2053
2054         if (!context || !context->internal->initialized) {
2055                 errno = EINVAL;  /* Best I can think of ... */
2056                 TALLOC_FREE(frame);
2057                 return -1;
2058         }
2059
2060         if (!fname) {
2061                 errno = EINVAL;
2062                 TALLOC_FREE(frame);
2063                 return -1;
2064         }
2065
2066         DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
2067
2068         if (SMBC_parse_path(frame,
2069                             context,
2070                             fname,
2071                             &workgroup,
2072                             &server,
2073                             &port,
2074                             &share,
2075                             &path,
2076                             &user,
2077                             &password,
2078                             NULL)) {
2079                 errno = EINVAL;
2080                 TALLOC_FREE(frame);
2081                 return -1;
2082         }
2083
2084         if (!user || user[0] == '\0') {
2085                 user = talloc_strdup(frame, smbc_getUser(context));
2086                 if (!user) {
2087                         errno = ENOMEM;
2088                         TALLOC_FREE(frame);
2089                         return -1;
2090                 }
2091         }
2092
2093         srv = SMBC_server(frame, context, True,
2094                           server, port, share, &workgroup, &user, &password);
2095         if (!srv) {
2096                 TALLOC_FREE(frame);
2097                 return -1;  /* errno set by SMBC_server */
2098         }
2099
2100         if (! srv->no_nt_session) {
2101                 ipc_srv = SMBC_attr_server(frame, context, server, port, share,
2102                                            &workgroup, &user, &password);
2103                 /*
2104                  * SMBC_attr_server() can cause the original
2105                  * server to be removed from the cache.
2106                  * If so we must error out here as the srv
2107                  * pointer has been freed.
2108                  */
2109                 if (smbc_getFunctionGetCachedServer(context)(context,
2110                                 server,
2111                                 share,
2112                                 workgroup,
2113                                 user) != srv) {
2114 #if defined(ECONNRESET)
2115                         errno = ECONNRESET;
2116 #else
2117                         errno = ETIMEDOUT;
2118 #endif
2119                         TALLOC_FREE(frame);
2120                         return -1;
2121                 }
2122                 if (! ipc_srv) {
2123                         srv->no_nt_session = True;
2124                 }
2125         } else {
2126                 ipc_srv = NULL;
2127         }
2128
2129         /* Determine whether to use old-style or new-style attribute names */
2130         if (context->internal->full_time_names) {
2131                 /* new-style names */
2132                 attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
2133                 attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
2134                 attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
2135                 attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
2136         } else {
2137                 /* old-style names */
2138                 attr_strings.create_time_attr = NULL;
2139                 attr_strings.access_time_attr = "system.dos_attr.A_TIME";
2140                 attr_strings.write_time_attr = "system.dos_attr.M_TIME";
2141                 attr_strings.change_time_attr = "system.dos_attr.C_TIME";
2142         }
2143
2144         /* Are they requesting a supported attribute? */
2145         if (strcasecmp_m(name, "system.*") == 0 ||
2146             strncasecmp_m(name, "system.*!", 9) == 0 ||
2147             strcasecmp_m(name, "system.*+") == 0 ||
2148             strncasecmp_m(name, "system.*+!", 10) == 0 ||
2149             strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
2150             strncasecmp_m(name, "system.nt_sec_desc.*!", 21) == 0 ||
2151             strcasecmp_m(name, "system.nt_sec_desc.*+") == 0 ||
2152             strncasecmp_m(name, "system.nt_sec_desc.*+!", 22) == 0 ||
2153             strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
2154             strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
2155             strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0 ||
2156             strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
2157             strcasecmp_m(name, "system.nt_sec_desc.group+") == 0 ||
2158             strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
2159             strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0 ||
2160             strcasecmp_m(name, "system.dos_attr.*") == 0 ||
2161             strncasecmp_m(name, "system.dos_attr.*!", 18) == 0 ||
2162             strcasecmp_m(name, "system.dos_attr.mode") == 0 ||
2163             strcasecmp_m(name, "system.dos_attr.size") == 0 ||
2164             (attr_strings.create_time_attr != NULL &&
2165              strcasecmp_m(name, attr_strings.create_time_attr) == 0) ||
2166             strcasecmp_m(name, attr_strings.access_time_attr) == 0 ||
2167             strcasecmp_m(name, attr_strings.write_time_attr) == 0 ||
2168             strcasecmp_m(name, attr_strings.change_time_attr) == 0 ||
2169             strcasecmp_m(name, "system.dos_attr.inode") == 0) {
2170
2171                 /* Yup. */
2172                 const char *filename = name;
2173                 ret = cacl_get(context, talloc_tos(), srv,
2174                                ipc_srv == NULL ? NULL : ipc_srv->cli,
2175                                &ipc_srv->pol, path,
2176                                filename,
2177                                discard_const_p(char, value),
2178                                size);
2179                 TALLOC_FREE(frame);
2180                 /*
2181                  * static function cacl_get returns a value greater than zero
2182                  * which is needed buffer size needed when size_t is 0.
2183                  */
2184                 return ret;
2185         }
2186
2187         /* Unsupported attribute name */
2188         errno = EINVAL;
2189         TALLOC_FREE(frame);
2190         return -1;
2191 }
2192
2193
2194 int
2195 SMBC_removexattr_ctx(SMBCCTX *context,
2196                      const char *fname,
2197                      const char *name)
2198 {
2199         int ret;
2200         SMBCSRV *srv = NULL;
2201         SMBCSRV *ipc_srv = NULL;
2202         char *server = NULL;
2203         char *share = NULL;
2204         char *user = NULL;
2205         char *password = NULL;
2206         char *workgroup = NULL;
2207         char *path = NULL;
2208         uint16_t port = 0;
2209         TALLOC_CTX *frame = talloc_stackframe();
2210
2211         if (!context || !context->internal->initialized) {
2212                 errno = EINVAL;  /* Best I can think of ... */
2213                 TALLOC_FREE(frame);
2214                 return -1;
2215         }
2216
2217         if (!fname) {
2218                 errno = EINVAL;
2219                 TALLOC_FREE(frame);
2220                 return -1;
2221         }
2222
2223         DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
2224
2225         if (SMBC_parse_path(frame,
2226                             context,
2227                             fname,
2228                             &workgroup,
2229                             &server,
2230                             &port,
2231                             &share,
2232                             &path,
2233                             &user,
2234                             &password,
2235                             NULL)) {
2236                 errno = EINVAL;
2237                 TALLOC_FREE(frame);
2238                 return -1;
2239         }
2240
2241         if (!user || user[0] == (char)0) {
2242                 user = talloc_strdup(frame, smbc_getUser(context));
2243                 if (!user) {
2244                         errno = ENOMEM;
2245                         TALLOC_FREE(frame);
2246                         return -1;
2247                 }
2248         }
2249
2250         srv = SMBC_server(frame, context, True,
2251                           server, port, share, &workgroup, &user, &password);
2252         if (!srv) {
2253                 TALLOC_FREE(frame);
2254                 return -1;  /* errno set by SMBC_server */
2255         }
2256
2257         if (! srv->no_nt_session) {
2258                 int saved_errno;
2259                 ipc_srv = SMBC_attr_server(frame, context, server, port, share,
2260                                            &workgroup, &user, &password);
2261                 saved_errno = errno;
2262                 /*
2263                  * SMBC_attr_server() can cause the original
2264                  * server to be removed from the cache.
2265                  * If so we must error out here as the srv
2266                  * pointer has been freed.
2267                  */
2268                 if (smbc_getFunctionGetCachedServer(context)(context,
2269                                 server,
2270                                 share,
2271                                 workgroup,
2272                                 user) != srv) {
2273 #if defined(ECONNRESET)
2274                         errno = ECONNRESET;
2275 #else
2276                         errno = ETIMEDOUT;
2277 #endif
2278                         TALLOC_FREE(frame);
2279                         return -1;
2280                 }
2281                 if (! ipc_srv) {
2282                         errno = saved_errno;
2283                         srv->no_nt_session = True;
2284                 }
2285         } else {
2286                 ipc_srv = NULL;
2287         }
2288
2289         if (! ipc_srv) {
2290                 TALLOC_FREE(frame);
2291                 return -1; /* errno set by SMBC_attr_server */
2292         }
2293
2294         /* Are they asking to set the entire ACL? */
2295         if (strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
2296             strcasecmp_m(name, "system.nt_sec_desc.*+") == 0) {
2297
2298                 /* Yup. */
2299                 ret = cacl_set(context, talloc_tos(), srv->cli,
2300                                ipc_srv->cli, &ipc_srv->pol, path,
2301                                NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
2302                 TALLOC_FREE(frame);
2303                 return ret;
2304         }
2305
2306         /*
2307          * Are they asking to remove one or more specific security descriptor
2308          * attributes?
2309          */
2310         if (strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
2311             strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
2312             strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0 ||
2313             strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
2314             strcasecmp_m(name, "system.nt_sec_desc.group+") == 0 ||
2315             strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
2316             strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0) {
2317
2318                 /* Yup. */
2319                 ret = cacl_set(context, talloc_tos(), srv->cli,
2320                                ipc_srv->cli, &ipc_srv->pol, path,
2321                                discard_const_p(char, name) + 19,
2322                                SMBC_XATTR_MODE_REMOVE, 0);
2323                 TALLOC_FREE(frame);
2324                 return ret;
2325         }
2326
2327         /* Unsupported attribute name */
2328         errno = EINVAL;
2329         TALLOC_FREE(frame);
2330         return -1;
2331 }
2332
2333 int
2334 SMBC_listxattr_ctx(SMBCCTX *context,
2335                    const char *fname,
2336                    char *list,
2337                    size_t size)
2338 {
2339         /*
2340          * This isn't quite what listxattr() is supposed to do.  This returns
2341          * the complete set of attribute names, always, rather than only those
2342          * attribute names which actually exist for a file.  Hmmm...
2343          */
2344         size_t retsize;
2345         static const char supported_old[] =
2346                 "system.*\0"
2347                 "system.*+\0"
2348                 "system.nt_sec_desc.revision\0"
2349                 "system.nt_sec_desc.owner\0"
2350                 "system.nt_sec_desc.owner+\0"
2351                 "system.nt_sec_desc.group\0"
2352                 "system.nt_sec_desc.group+\0"
2353                 "system.nt_sec_desc.acl.*\0"
2354                 "system.nt_sec_desc.acl\0"
2355                 "system.nt_sec_desc.acl+\0"
2356                 "system.nt_sec_desc.*\0"
2357                 "system.nt_sec_desc.*+\0"
2358                 "system.dos_attr.*\0"
2359                 "system.dos_attr.mode\0"
2360                 "system.dos_attr.c_time\0"
2361                 "system.dos_attr.a_time\0"
2362                 "system.dos_attr.m_time\0"
2363                 ;
2364         static const char supported_new[] =
2365                 "system.*\0"
2366                 "system.*+\0"
2367                 "system.nt_sec_desc.revision\0"
2368                 "system.nt_sec_desc.owner\0"
2369                 "system.nt_sec_desc.owner+\0"
2370                 "system.nt_sec_desc.group\0"
2371                 "system.nt_sec_desc.group+\0"
2372                 "system.nt_sec_desc.acl.*\0"
2373                 "system.nt_sec_desc.acl\0"
2374                 "system.nt_sec_desc.acl+\0"
2375                 "system.nt_sec_desc.*\0"
2376                 "system.nt_sec_desc.*+\0"
2377                 "system.dos_attr.*\0"
2378                 "system.dos_attr.mode\0"
2379                 "system.dos_attr.create_time\0"
2380                 "system.dos_attr.access_time\0"
2381                 "system.dos_attr.write_time\0"
2382                 "system.dos_attr.change_time\0"
2383                 ;
2384         const char * supported;
2385
2386         if (context->internal->full_time_names) {
2387                 supported = supported_new;
2388                 retsize = sizeof(supported_new);
2389         } else {
2390                 supported = supported_old;
2391                 retsize = sizeof(supported_old);
2392         }
2393
2394         if (size == 0) {
2395                 return retsize;
2396         }
2397
2398         if (retsize > size) {
2399                 errno = ERANGE;
2400                 return -1;
2401         }
2402
2403         /* this can't be strcpy() because there are embedded null characters */
2404         memcpy(list, supported, retsize);
2405         return retsize;
2406 }