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