getcifsacl: use "size" instead of reconverting original field to host endian
[jlayton/cifs-utils.git] / getcifsacl.c
1 /*
2 * getcifsacl utility
3 *
4 * Copyright (C) Shirish Pargaonkar (shirishp@us.ibm.com) 2011
5 *
6 * Used to display a security descriptor including ACL of a file object
7 * that belongs to a share mounted using option cifsacl.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif /* HAVE_CONFIG_H */
25
26 #include <string.h>
27 #include <getopt.h>
28 #include <syslog.h>
29 #include <stdint.h>
30 #include <stdbool.h>
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stddef.h>
35 #include <errno.h>
36 #include <limits.h>
37 #include <wbclient.h>
38 #include <ctype.h>
39 #include <sys/xattr.h>
40 #include "cifsacl.h"
41
42 static const char *prog;
43
44 static void
45 print_each_ace_mask(uint32_t mask)
46 {
47         if ((mask & ALL_READ_BITS) && ((mask & EREAD) != EREAD &&
48                         (mask & OREAD) != OREAD && (mask & BREAD) != BREAD)) {
49                 printf("0x%x", mask);
50                 return;
51         }
52
53         if ((mask & ALL_WRITE_BITS) && (mask & EWRITE) != EWRITE) {
54                 printf("0x%x", mask);
55                 return;
56         }
57
58         if ((mask & EREAD) == EREAD || (mask & OREAD) == OREAD ||
59                         (mask & BREAD) == BREAD)
60                 printf("R");
61         if ((mask & EWRITE) == EWRITE)
62                 printf("W");
63         if ((mask & EXEC) == EXEC)
64                 printf("X");
65         if ((mask & DELETE) == DELETE)
66                 printf("D");
67         if ((mask & WRITE_DAC) == WRITE_DAC)
68                 printf("P");
69         if ((mask & WRITE_OWNER) == WRITE_OWNER)
70                 printf("O");
71 }
72
73 static void
74 print_ace_mask(uint32_t mask, int raw)
75 {
76         if (raw) {
77                 printf("0x%x\n", mask);
78                 return;
79         }
80
81         if (mask == FULL_CONTROL)
82                 printf("FULL");
83         else if (mask == CHANGE)
84                 printf("CHANGE");
85         else if (mask == DELETE)
86                 printf("D");
87         else if (mask == EREAD)
88                 printf("READ");
89         else if (mask & DELDHLD)
90                 printf("0x%x", mask);
91         else
92                 print_each_ace_mask(mask);
93
94         printf("\n");
95         return;
96 }
97
98 static void
99 print_ace_flags(uint8_t flags, int raw)
100 {
101         bool mflags = false;
102
103         if (raw) {
104                 printf("0x%x", flags);
105                 return;
106         }
107
108         if (flags & OBJECT_INHERIT_FLAG) {
109                 if (mflags)
110                         printf("|");
111                 else
112                         mflags = true;
113                 printf("OI");
114         }
115         if (flags & CONTAINER_INHERIT_FLAG) {
116                 if (mflags)
117                         printf("|");
118                 else
119                         mflags = true;
120                 printf("CI");
121         }
122         if (flags & NO_PROPAGATE_INHERIT_FLAG) {
123                 if (mflags)
124                         printf("|");
125                 else
126                         mflags = true;
127                 printf("NP");
128         }
129         if (flags & INHERIT_ONLY_FLAG) {
130                 if (mflags)
131                         printf("|");
132                 else
133                         mflags = true;
134                 printf("IO");
135         }
136         if (flags & INHERITED_ACE_FLAG) {
137                 if (mflags)
138                         printf("|");
139                 else
140                         mflags = true;
141                 printf("I");
142         }
143
144         if (!mflags)
145                 printf("0x0");
146 }
147
148 static void
149 print_ace_type(uint8_t acetype, int raw)
150 {
151         if (raw) {
152                 printf("0x%x", acetype);
153                 return;
154         }
155
156         switch (acetype) {
157         case ACCESS_ALLOWED:
158                 printf("ALLOWED");
159                 break;
160         case ACCESS_DENIED:
161                 printf("DENIED");
162                 break;
163         case ACCESS_ALLOWED_OBJECT:
164                 printf("OBJECT_ALLOWED");
165                 break;
166         case ACCESS_DENIED_OBJECT:
167                 printf("OBJECT_DENIED");
168                 break;
169         default:
170                 printf("UNKNOWN");
171                 break;
172         }
173 }
174
175 /*
176  * Winbind keeps wbcDomainSid fields in host-endian. Copy fields from the
177  * csid to the wsid, while converting the subauthority fields from LE.
178  */
179 static void
180 csid_to_wsid(struct wbcDomainSid *wsid, const struct cifs_sid *csid)
181 {
182         int i;
183
184         wsid->sid_rev_num = csid->revision;
185         wsid->num_auths = csid->num_subauth;
186         for (i = 0; i < NUM_AUTHS; i++)
187                 wsid->id_auth[i] = csid->authority[i];
188         for (i = 0; i < csid->num_subauth; i++)
189                 wsid->sub_auths[i] = le32toh(csid->sub_auth[i]);
190 }
191
192 static void
193 print_sid(struct cifs_sid *sidptr, int raw)
194 {
195         int i;
196         wbcErr rc;
197         char *domain_name = NULL;
198         char *sidname = NULL;
199         enum wbcSidType sntype;
200         unsigned long long id_auth_val;
201         struct wbcDomainSid wsid;
202
203         csid_to_wsid(&wsid, sidptr);
204
205         if (raw)
206                 goto print_sid_raw;
207
208         rc = wbcLookupSid(&wsid, &domain_name, &sidname, &sntype);
209         if (WBC_ERROR_IS_OK(rc)) {
210                 printf("%s", domain_name);
211                 if (strlen(domain_name))
212                         printf("%c", '\\');
213                 printf("%s", sidname);
214                 return;
215         }
216
217 print_sid_raw:
218         printf("S-%hhu", wsid.sid_rev_num);
219
220         id_auth_val = (unsigned long long)wsid.id_auth[5];
221         id_auth_val += (unsigned long long)wsid.id_auth[4] << 8;
222         id_auth_val += (unsigned long long)wsid.id_auth[3] << 16;
223         id_auth_val += (unsigned long long)wsid.id_auth[2] << 24;
224         id_auth_val += (unsigned long long)wsid.id_auth[1] << 32;
225         id_auth_val += (unsigned long long)wsid.id_auth[0] << 48;
226
227         /*
228          * MS-DTYP states that if the authority is >= 2^32, then it should be
229          * expressed as a hex value.
230          */
231         if (id_auth_val <= UINT_MAX)
232                 printf("-%llu", id_auth_val);
233         else
234                 printf("-0x%llx", id_auth_val);
235
236         for (i = 0; i < wsid.num_auths; i++)
237                 printf("-%u", wsid.sub_auths[i]);
238 }
239
240 static void
241 print_ace(struct cifs_ace *pace, char *end_of_acl, int raw)
242 {
243         uint16_t size;
244
245         /* make sure we can safely get to "size" */
246         if (end_of_acl < (char *)pace + offsetof(struct cifs_ace, size) + 1)
247                 return;
248
249         size = le16toh(pace->size);
250
251         /* 16 == size of cifs_ace when cifs_sid has no subauths */
252         if (size < 16)
253                 return;
254
255         /* validate that we do not go past end of acl */
256         if (end_of_acl < (char *)pace + size)
257                 return;
258
259         printf("ACL:");
260         print_sid((struct cifs_sid *)&pace->sid, raw);
261         printf(":");
262         print_ace_type(pace->type, raw);
263         printf("/");
264         print_ace_flags(pace->flags, raw);
265         printf("/");
266         print_ace_mask(le32toh(pace->access_req), raw);
267
268         return;
269 }
270
271 static void
272 parse_dacl(struct cifs_ctrl_acl *pdacl, char *end_of_acl, int raw)
273 {
274         int i;
275         int num_aces = 0;
276         int acl_size;
277         char *acl_base;
278         struct cifs_ace *pace;
279
280         if (!pdacl)
281                 return;
282
283         if (end_of_acl < (char *)pdacl + le16toh(pdacl->size))
284                 return;
285
286         acl_base = (char *)pdacl;
287         acl_size = sizeof(struct cifs_ctrl_acl);
288
289         num_aces = le32toh(pdacl->num_aces);
290         if (num_aces  > 0) {
291                 for (i = 0; i < num_aces; ++i) {
292                         pace = (struct cifs_ace *) (acl_base + acl_size);
293                         print_ace(pace, end_of_acl, raw);
294                         acl_base = (char *)pace;
295                         acl_size = le16toh(pace->size);
296                 }
297         }
298
299         return;
300 }
301
302 static int
303 parse_sid(struct cifs_sid *psid, char *end_of_acl, char *title, int raw)
304 {
305         if (end_of_acl < (char *)psid + 8)
306                 return -EINVAL;
307
308         if (title)
309                 printf("%s:", title);
310         print_sid((struct cifs_sid *)psid, raw);
311         printf("\n");
312
313         return 0;
314 }
315
316 static int
317 parse_sec_desc(struct cifs_ntsd *pntsd, ssize_t acl_len, int raw)
318 {
319         int rc;
320         uint32_t dacloffset;
321         char *end_of_acl = ((char *)pntsd) + acl_len;
322         struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
323         struct cifs_ctrl_acl *dacl_ptr; /* no need for SACL ptr */
324
325         if (pntsd == NULL)
326                 return -EIO;
327
328         owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
329                                 le32toh(pntsd->osidoffset));
330         group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
331                                 le32toh(pntsd->gsidoffset));
332         dacloffset = le32toh(pntsd->dacloffset);
333         dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
334         printf("REVISION:0x%x\n", le16toh(pntsd->revision));
335         printf("CONTROL:0x%x\n", le16toh(pntsd->type));
336
337         rc = parse_sid(owner_sid_ptr, end_of_acl, "OWNER", raw);
338         if (rc)
339                 return rc;
340
341         rc = parse_sid(group_sid_ptr, end_of_acl, "GROUP", raw);
342         if (rc)
343                 return rc;
344
345         if (dacloffset)
346                 parse_dacl(dacl_ptr, end_of_acl, raw);
347         else
348                 printf("No ACL\n"); /* BB grant all or default perms? */
349
350         return 0;
351 }
352
353 static void
354 getcifsacl_usage(void)
355 {
356         fprintf(stderr,
357         "%s: Display CIFS/NTFS ACL in a security descriptor of a file object\n",
358                 prog);
359         fprintf(stderr, "Usage: %s [option] <file_name>\n", prog);
360         fprintf(stderr, "Valid options:\n");
361         fprintf(stderr, "\t-v   Version of the program\n");
362         fprintf(stderr, "\n");
363         fprintf(stderr, "\t-r   Display raw values of the ACE fields\n");
364         fprintf(stderr, "\nRefer to getcifsacl(1) manpage for details\n");
365 }
366
367 int
368 main(const int argc, char *const argv[])
369 {
370         int c, raw = 0;
371         ssize_t attrlen;
372         size_t bufsize = BUFSIZE;
373         char *filename, *attrval;
374
375         prog = basename(argv[0]);
376         openlog(prog, 0, LOG_DAEMON);
377
378         while ((c = getopt_long(argc, argv, "r:v", NULL, NULL)) != -1) {
379                 switch (c) {
380                 case 'v':
381                         printf("Version: %s\n", VERSION);
382                         goto out;
383                 case 'r':
384                         raw = 1;
385                         break;
386                 default:
387                         break;
388                 }
389         }
390
391         if (raw && argc == 3)
392                 filename = argv[2];
393         else if (argc == 2)
394                 filename = argv[1];
395         else {
396                 getcifsacl_usage();
397                 return 0;
398         }
399
400 cifsacl:
401         if (bufsize >= XATTR_SIZE_MAX) {
402                 printf("buffer to allocate exceeds max size of %d\n",
403                                 XATTR_SIZE_MAX);
404                 return -1;
405         }
406
407         attrval = malloc(bufsize * sizeof(char));
408         if (!attrval) {
409                 printf("error allocating memory for attribute value buffer\n");
410                 return -1;
411         }
412
413         attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
414         if (attrlen == -1) {
415                 if (errno == ERANGE) {
416                         free(attrval);
417                         bufsize += BUFSIZE;
418                         goto cifsacl;
419                 } else
420                         printf("getxattr error: %d\n", errno);
421         }
422
423         parse_sec_desc((struct cifs_ntsd *)attrval, attrlen, raw);
424
425         free(attrval);
426
427 out:
428         return 0;
429 }