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