mount.cifs: fix max buffer size when parsing snapshot option
[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 <stdint.h>
29 #include <stdbool.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stddef.h>
34 #include <errno.h>
35 #include <limits.h>
36 #include <ctype.h>
37 #include <sys/xattr.h>
38 #include "cifsacl.h"
39 #include "idmap_plugin.h"
40 #include <ftw.h>
41
42 static void *plugin_handle;
43 static bool plugin_loaded;
44 static char *execname;
45 static bool raw = false;
46
47 static void
48 print_each_ace_mask(uint32_t mask)
49 {
50         if ((mask & ALL_ACCESS_BITS) == ALL_ACCESS_BITS) {
51                 printf("RWXDPO");
52                 return;
53         }
54
55         if ((mask & ALL_READ_BITS) && ((mask & EREAD) != EREAD &&
56                         (mask & OREAD) != OREAD && (mask & BREAD) != BREAD)) {
57                 printf("0x%x", mask);
58                 return;
59         }
60
61         if ((mask & ALL_WRITE_BITS) && (mask & EWRITE) != EWRITE) {
62                 printf("0x%x", mask);
63                 return;
64         }
65
66         if ((mask & EREAD) == EREAD || (mask & OREAD) == OREAD ||
67                         (mask & BREAD) == BREAD)
68                 printf("R");
69         if ((mask & EWRITE) == EWRITE)
70                 printf("W");
71         if ((mask & EXEC) == EXEC)
72                 printf("X");
73         if ((mask & DELETE) == DELETE)
74                 printf("D");
75         if ((mask & WRITE_DAC) == WRITE_DAC)
76                 printf("P");
77         if ((mask & WRITE_OWNER) == WRITE_OWNER)
78                 printf("O");
79 }
80
81 static void
82 print_ace_mask(uint32_t mask, int raw, ace_kinds ace_kind)
83 {
84         if (raw) {
85                 printf("0x%x\n", mask);
86                 return;
87         }
88
89         switch (ace_kind) {
90         case ACE_KIND_SACL:
91                 if (mask == FULL_CONTROL)
92                         printf("FULL");
93                 else if (mask == CHANGE)
94                         printf("CHANGE");
95                 else if (mask == DELETE)
96                         printf("D");
97                 else if (mask == EREAD)
98                         printf("READ");
99                 else
100                         print_each_ace_mask(mask);
101         break;
102         case ACE_KIND_DACL:
103         default:
104                 if (mask == FULL_CONTROL)
105                         printf("FULL");
106                 else if (mask == CHANGE)
107                         printf("CHANGE");
108                 else if (mask == DELETE)
109                         printf("D");
110                 else if (mask == EREAD)
111                         printf("READ");
112                 else if (mask & DELDHLD)
113                         printf("0x%x", mask);
114                 else
115                         print_each_ace_mask(mask);
116         break;
117         }
118         printf("\n");
119         return;
120 }
121
122 static void
123 print_ace_flags(uint8_t flags, int raw, ace_kinds ace_kind)
124 {
125         bool mflags = false;
126
127         if (raw) {
128                 printf("0x%x", flags);
129                 return;
130         }
131
132         switch (ace_kind) {
133         case ACE_KIND_SACL:
134                 if (flags & SUCCESSFUL_ACCESS) {
135                         mflags = true;
136                         printf("SA");
137                 }
138                 if (flags & FAILED_ACCESS) {
139                         if (mflags)
140                                 printf("|");
141                         else
142                                 mflags = true;
143                         printf("FA");
144                 }
145                 break;
146         case ACE_KIND_DACL:
147                 if (flags & OBJECT_INHERIT_FLAG) {
148                         mflags = true;
149                         printf("OI");
150                 }
151                 if (flags & CONTAINER_INHERIT_FLAG) {
152                         if (mflags)
153                                 printf("|");
154                         else
155                                 mflags = true;
156                         printf("CI");
157                 }
158                 if (flags & NO_PROPAGATE_INHERIT_FLAG) {
159                         if (mflags)
160                                 printf("|");
161                         else
162                                 mflags = true;
163                         printf("NP");
164                 }
165                 if (flags & INHERIT_ONLY_FLAG) {
166                         if (mflags)
167                                 printf("|");
168                         else
169                                 mflags = true;
170                         printf("IO");
171                 }
172                 if (flags & INHERITED_ACE_FLAG) {
173                         if (mflags)
174                                 printf("|");
175                         else
176                                 mflags = true;
177                         printf("I");
178                 }
179                 break;
180         }
181
182         if (!mflags)
183                 printf("0x0");
184 }
185
186 static void
187 print_ace_type(uint8_t acetype, int raw)
188 {
189         if (raw) {
190                 printf("0x%x", acetype);
191                 return;
192         }
193
194         switch (acetype) {
195         case ACCESS_ALLOWED:
196                 printf("ALLOWED");
197                 break;
198         case ACCESS_DENIED:
199                 printf("DENIED");
200                 break;
201         case ACCESS_ALLOWED_OBJECT:
202                 printf("OBJECT_ALLOWED");
203                 break;
204         case ACCESS_DENIED_OBJECT:
205                 printf("OBJECT_DENIED");
206                 break;
207         case SYSTEM_AUDIT:
208                 printf("AUDIT");
209                 break;
210         case SYSTEM_AUDIT_OBJECT:
211                 printf("AUDIT_OBJECT");
212                 break;
213         case SYSTEM_AUDIT_CALLBACK:
214                 printf("AUDIT_CALLBACK");
215                 break;
216         case SYSTEM_AUDIT_CALLBACK_OBJECT:
217                 printf("AUDIT_CALLBACK_OBJECT");
218                 break;
219         case SYSTEM_MANDATORY_LABEL:
220                 printf("MANDATORY_LABEL");
221                 break;
222         case SYSTEM_RESOURCE_ATTRIBUTE:
223                 printf("RESOURCE_ATTRIBUTE");
224                 break;
225         case SYSTEM_SCOPED_POLICY_ID:
226                 printf("SCOPED_POLICY_ID");
227                 break;
228         default:
229                 printf("UNKNOWN");
230                 break;
231         }
232 }
233
234 static void
235 print_sid(struct cifs_sid *csid, int raw)
236 {
237         int i, rc;
238         char *name;
239         unsigned long long id_auth_val;
240
241         if (raw || !plugin_loaded)
242                 goto print_sid_raw;
243
244         rc = sid_to_str(plugin_handle, csid, &name);
245         if (rc)
246                 goto print_sid_raw;
247
248         printf("%s", name);
249         free(name);
250         return;
251
252 print_sid_raw:
253         printf("S-%hhu", csid->revision);
254
255         id_auth_val = (unsigned long long)csid->authority[5];
256         id_auth_val += (unsigned long long)csid->authority[4] << 8;
257         id_auth_val += (unsigned long long)csid->authority[3] << 16;
258         id_auth_val += (unsigned long long)csid->authority[2] << 24;
259         id_auth_val += (unsigned long long)csid->authority[1] << 32;
260         id_auth_val += (unsigned long long)csid->authority[0] << 40;
261
262         /*
263          * MS-DTYP states that if the authority is >= 2^32, then it should be
264          * expressed as a hex value.
265          */
266         if (id_auth_val <= UINT_MAX)
267                 printf("-%llu", id_auth_val);
268         else
269                 printf("-0x%llx", id_auth_val);
270
271         for (i = 0; i < csid->num_subauth; i++)
272                 printf("-%u", le32toh(csid->sub_auth[i]));
273 }
274
275 static void
276 print_ace(struct cifs_ace *pace, char *end_of_acl, int raw, ace_kinds ace_kind)
277 {
278         uint16_t size;
279
280         /* make sure we can safely get to "size" */
281         if (end_of_acl < (char *)pace + offsetof(struct cifs_ace, size) + 1)
282                 return;
283
284         size = le16toh(pace->size);
285
286         /* 16 == size of cifs_ace when cifs_sid has no subauths */
287         if (size < 16)
288                 return;
289
290         /* validate that we do not go past end of acl */
291         if (end_of_acl < (char *)pace + size)
292                 return;
293
294         printf("ACL:");
295         print_sid((struct cifs_sid *)&pace->sid, raw);
296         printf(":");
297         print_ace_type(pace->type, raw);
298         printf("/");
299         print_ace_flags(pace->flags, raw, ace_kind);
300         printf("/");
301         print_ace_mask(le32toh(pace->access_req), raw, ace_kind);
302
303         return;
304 }
305
306 static void
307   parse_acl(struct cifs_ctrl_acl *pacl, char *end_of_acl, int raw, ace_kinds ace_kind)
308 {
309         int i;
310         int num_aces = 0;
311         int acl_size;
312         char *acl_base;
313         struct cifs_ace *pace;
314
315         if (!pacl)
316                 return;
317
318         if (end_of_acl < (char *)pacl + le16toh(pacl->size))
319                 return;
320
321         acl_base = (char *)pacl;
322         acl_size = sizeof(struct cifs_ctrl_acl);
323
324         num_aces = le32toh(pacl->num_aces);
325         if (num_aces  > 0) {
326                 for (i = 0; i < num_aces; ++i) {
327                         pace = (struct cifs_ace *) (acl_base + acl_size);
328                         print_ace(pace, end_of_acl, raw, ace_kind);
329                         acl_base = (char *)pace;
330                         acl_size = le16toh(pace->size);
331                 }
332         }
333
334         return;
335 }
336
337 static int
338 parse_sid(struct cifs_sid *psid, char *end_of_acl, char *title, int raw)
339 {
340         if (end_of_acl < (char *)psid + 8)
341                 return -EINVAL;
342
343         if (title)
344                 printf("%s:", title);
345         print_sid((struct cifs_sid *)psid, raw);
346         printf("\n");
347
348         return 0;
349 }
350
351 static int
352 parse_sec_desc(struct cifs_ntsd *pntsd, ssize_t acl_len, int raw)
353 {
354         int rc;
355         uint32_t dacloffset, sacloffset;
356         char *end_of_acl = ((char *)pntsd) + acl_len;
357         struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
358         struct cifs_ctrl_acl *dacl_ptr, *sacl_ptr;
359
360         if (pntsd == NULL)
361                 return -EIO;
362
363         owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
364                                 le32toh(pntsd->osidoffset));
365         group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
366                                 le32toh(pntsd->gsidoffset));
367         dacloffset = le32toh(pntsd->dacloffset);
368         dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
369         sacloffset = le32toh(pntsd->sacloffset);
370         sacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + sacloffset);
371
372         printf("REVISION:0x%x\n", le16toh(pntsd->revision));
373         printf("CONTROL:0x%x\n", le16toh(pntsd->type));
374
375         rc = parse_sid(owner_sid_ptr, end_of_acl, "OWNER", raw);
376         if (rc)
377                 return rc;
378
379         rc = parse_sid(group_sid_ptr, end_of_acl, "GROUP", raw);
380         if (rc)
381                 return rc;
382
383         if (dacloffset) {
384                 printf("DACL:\n");
385                 parse_acl(dacl_ptr, end_of_acl, raw, ACE_KIND_DACL);
386         } else {
387                 printf("No DACL\n"); /* BB grant all or default perms? */
388         }
389
390         if (sacloffset) {
391                 printf("SACL:\n");
392                 parse_acl(sacl_ptr, end_of_acl, raw, ACE_KIND_SACL);
393         } else {
394                 printf("No SACL\n");
395         }
396
397         return 0;
398 }
399
400 static void
401 getcifsacl_usage(const char *prog)
402 {
403         fprintf(stderr,
404         "%s: Display CIFS/NTFS ACL in a security descriptor of a file object\n",
405                 prog);
406         fprintf(stderr, "Usage: %s [option] <file_name1> [<file_name2>,<file_name3>,...]\n", prog);
407         fprintf(stderr, "Valid options:\n");
408         fprintf(stderr, "\t-h   Display this help text\n");
409         fprintf(stderr, "\n");
410         fprintf(stderr, "\t-v   Version of the program\n");
411         fprintf(stderr, "\n");
412         fprintf(stderr, "\t-R   recurse into subdirectories\n");
413         fprintf(stderr, "\n");
414         fprintf(stderr, "\t-r   Display raw values of the ACE fields\n");
415         fprintf(stderr, "\nRefer to getcifsacl(1) manpage for details\n");
416 }
417
418 static int
419 getcifsacl(const char *filename)
420 {
421         ssize_t attrlen;
422         size_t bufsize = BUFSIZE;
423         char *attrval;
424         int rc = 0;
425         /* use attribute name to fetch the whole descriptor */
426         char *attrname = ATTRNAME_NTSD_FULL;
427
428 cifsacl:
429         if (bufsize >= XATTR_SIZE_MAX) {
430                 fprintf(stderr, "buffer to allocate exceeds max size of %d\n",
431                         XATTR_SIZE_MAX);
432                 exit(1);
433         }
434
435         attrval = malloc(bufsize * sizeof(char));
436         if (!attrval) {
437                 fprintf(stderr, "error allocating memory for attribute value buffer\n");
438                 exit(1);
439         }
440
441 getxattr:
442         attrlen = getxattr(filename, attrname, attrval, bufsize);
443         if (attrlen == -1) {
444                 if (errno == ERANGE) {
445                         free(attrval);
446                         bufsize += BUFSIZE;
447                         goto cifsacl;
448                 } else if (errno == EIO && !(strcmp(attrname, ATTRNAME_NTSD_FULL))) {
449                         /*
450                          * attempt to fetch SACL in addition to owner and DACL via
451                          * ATTRNAME_NTSD_FULL, fall back to owner/DACL via
452                          * ATTRNAME_ACL if not allowed
453                          * CIFS client maps STATUS_PRIVILEGE_NOT_HELD to EIO
454                          */
455                         fprintf(stderr, "WARNING: Insufficient priviledges to fetch SACL for %s\n",
456                                 filename);
457                         fprintf(stderr, "          Fetching owner info and DACL only\n");
458                         attrname = ATTRNAME_ACL;
459                         goto getxattr;
460                 } else if (errno == EOPNOTSUPP && !(strcmp(attrname, ATTRNAME_NTSD_FULL))) {
461                         /*
462                          * no support for fetching SACL, fall back to owner/DACL via
463                          * ATTRNAME_ACL
464                          */
465                         fprintf(stderr, "WARNING: CIFS client does not support fetching SACL for %s\n",
466                                 filename);
467                         fprintf(stderr, "          Fetching owner info and DACL only\n");
468                         attrname = ATTRNAME_ACL;
469                         goto getxattr;
470                 } else {
471                         fprintf(stderr, "Failed to getxattr %s: %s\n", filename,
472                                 strerror(errno));
473                         rc = -1;
474                 }
475         }
476
477         if (rc == 0) {
478                 printf("# filename: %s\n", filename);
479                 parse_sec_desc((struct cifs_ntsd *)attrval, attrlen, raw);
480                 printf("\n");
481         }
482         free(attrval);
483         return rc;
484 }
485
486 static int recursive(const char *filename, const struct stat *sb, int tflag, struct FTW *ftwbuf)
487 {
488         (void)sb;
489         (void)tflag;
490         (void)ftwbuf;
491         return getcifsacl(filename);
492 }
493
494 int
495 main(const int argc, char *const argv[])
496 {
497         int c, ret = 0;
498         execname = basename(argv[0]);
499         int do_recursive = 0;
500         int tmp_rc;
501
502         if (argc < 2) {
503                 fprintf(stderr, "%s: you must specify a filename.\n", execname);
504                 printf("Try `getcifsacl -h' for more information.\n");
505                 goto out;
506         }
507
508         while ((c = getopt_long(argc, argv, "Rrhv", NULL, NULL)) != -1) {
509                 switch (c) {
510                 case 'v':
511                         printf("Version: %s\n", VERSION);
512                         goto out;
513                 case 'r':
514                         raw = true;
515                         break;
516                 case 'R':
517                         do_recursive = 1;
518                         break;
519                 default:
520                         getcifsacl_usage(execname);
521                         goto out;
522                 }
523         }
524
525         if (optind >= argc) {
526                 printf("you must specify a filename after options.\n");
527                 printf("Usage: getcifsacl [option] <file_name1> [<file_name2>,<file_name3>,...]\n");
528                 goto out;
529         }
530
531         if (!raw && !plugin_loaded) {
532                 ret = init_plugin(&plugin_handle);
533                 if (ret)
534                         printf("WARNING: unable to initialize idmapping plugin: %s\n",
535                                 plugin_errmsg);
536                 else
537                         plugin_loaded = true;
538         }
539
540         ret = 0;
541         for(; optind < argc; optind++) {
542                 if (do_recursive) {
543                         if (nftw(argv[optind], recursive, 20, 0) == -1)
544                                 fprintf(stderr, "Invalid filename %s: %s\n", argv[optind], strerror(errno));
545                 } else {
546                         tmp_rc = getcifsacl(argv[optind]);
547                         if (tmp_rc && !ret)
548                                 ret = tmp_rc;
549                 }
550         }
551
552 out:
553         if (plugin_loaded)
554                 exit_plugin(plugin_handle);
555         return ret;
556 }