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