8cbdb1d6585b2b17f33f7153a779df88236da03c
[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 <errno.h>
35 #include <limits.h>
36 #include <wbclient.h>
37 #include <ctype.h>
38 #include <sys/xattr.h>
39 #include "cifsacl.h"
40
41 static const char *prog = "getcifsacl";
42
43 static void
44 print_each_ace_mask(uint32_t mask)
45 {
46         if ((mask & ALL_READ_BITS) && ((mask & EREAD) != EREAD &&
47                         (mask & OREAD) != OREAD && (mask & BREAD) != BREAD)) {
48                 printf("0x%x", mask);
49                 return;
50         }
51
52         if ((mask & ALL_WRITE_BITS) && (mask & EWRITE) != EWRITE) {
53                 printf("0x%x", mask);
54                 return;
55         }
56
57         if ((mask & EREAD) == EREAD || (mask & OREAD) == OREAD ||
58                         (mask & BREAD) == BREAD)
59                 printf("R");
60         if ((mask & EWRITE) == EWRITE)
61                 printf("W");
62         if ((mask & EXEC) == EXEC)
63                 printf("X");
64         if ((mask & DELETE) == DELETE)
65                 printf("D");
66         if ((mask & WRITE_DAC) == WRITE_DAC)
67                 printf("P");
68         if ((mask & WRITE_OWNER) == WRITE_OWNER)
69                 printf("O");
70 }
71
72 static void
73 print_ace_mask(uint32_t mask, int raw)
74 {
75         if (raw) {
76                 printf("0x%x\n", mask);
77                 return;
78         }
79
80         if (mask == FULL_CONTROL)
81                 printf("FULL");
82         else if (mask == CHANGE)
83                 printf("CHANGE");
84         else if (mask == DELETE)
85                 printf("D");
86         else if (mask == EREAD)
87                 printf("READ");
88         else if (mask & DELDHLD)
89                 printf("0x%x", mask);
90         else
91                 print_each_ace_mask(mask);
92
93         printf("\n");
94         return;
95 }
96
97 static void
98 print_ace_flags(uint8_t flags, int raw)
99 {
100         bool mflags = false;
101
102         if (raw) {
103                 printf("0x%x", flags);
104                 return;
105         }
106
107         if (flags & OBJECT_INHERIT_FLAG) {
108                 if (mflags)
109                         printf("|");
110                 else
111                         mflags = true;
112                 printf("OI");
113         }
114         if (flags & CONTAINER_INHERIT_FLAG) {
115                 if (mflags)
116                         printf("|");
117                 else
118                         mflags = true;
119                 printf("CI");
120         }
121         if (flags & NO_PROPAGATE_INHERIT_FLAG) {
122                 if (mflags)
123                         printf("|");
124                 else
125                         mflags = true;
126                 printf("NP");
127         }
128         if (flags & INHERIT_ONLY_FLAG) {
129                 if (mflags)
130                         printf("|");
131                 else
132                         mflags = true;
133                 printf("IO");
134         }
135         if (flags & INHERITED_ACE_FLAG) {
136                 if (mflags)
137                         printf("|");
138                 else
139                         mflags = true;
140                 printf("I");
141         }
142
143         if (!mflags)
144                 printf("0x0");
145 }
146
147 static void
148 print_ace_type(uint8_t acetype, int raw)
149 {
150         if (raw) {
151                 printf("0x%x", acetype);
152                 return;
153         }
154
155         switch (acetype) {
156         case ACCESS_ALLOWED:
157                 printf("ALLOWED");
158                 break;
159         case ACCESS_DENIED:
160                 printf("DENIED");
161                 break;
162         case ACCESS_ALLOWED_OBJECT:
163                 printf("OBJECT_ALLOWED");
164                 break;
165         case ACCESS_DENIED_OBJECT:
166                 printf("OBJECT_DENIED");
167                 break;
168         default:
169                 printf("UNKNOWN");
170                 break;
171         }
172 }
173
174 static void
175 print_sid(struct wbcDomainSid *sidptr, int raw)
176 {
177         int i;
178         int num_auths;
179         int num_auth = MAX_NUM_AUTHS;
180         wbcErr rc;
181         char *domain_name = NULL;
182         char *sidname = NULL;
183         enum wbcSidType sntype;
184
185         if (raw)
186                 goto print_sid_raw;
187
188         rc = wbcLookupSid(sidptr, &domain_name, &sidname, &sntype);
189         if (!rc) {
190                 printf("%s", domain_name);
191                 if (strlen(domain_name))
192                         printf("%c", '\\');
193                 printf("%s", sidname);
194                 return;
195         }
196
197 print_sid_raw:
198         num_auths = sidptr->num_auths;
199         printf("S");
200         printf("-%d", sidptr->sid_rev_num);
201         for (i = 0; i < num_auth; ++i)
202                 if (sidptr->id_auth[i])
203                         printf("-%d", sidptr->id_auth[i]);
204         for (i = 0; i < num_auths; i++)
205                 printf("-%u", le32toh(sidptr->sub_auths[i]));
206 }
207
208 static void
209 print_ace(struct cifs_ace *pace, char *end_of_acl, int raw)
210 {
211         /* validate that we do not go past end of acl */
212
213         if (le16toh(pace->size) < 16)
214                 return;
215
216         if (end_of_acl < (char *)pace + le16toh(pace->size))
217                 return;
218
219         printf("ACL:");
220         print_sid((struct wbcDomainSid *)&pace->sid, raw);
221         printf(":");
222         print_ace_type(pace->type, raw);
223         printf("/");
224         print_ace_flags(pace->flags, raw);
225         printf("/");
226         print_ace_mask(pace->access_req, raw);
227
228
229         return;
230 }
231
232 static void
233 parse_dacl(struct cifs_ctrl_acl *pdacl, char *end_of_acl, int raw)
234 {
235         int i;
236         int num_aces = 0;
237         int acl_size;
238         char *acl_base;
239         struct cifs_ace *pace;
240
241         if (!pdacl)
242                 return;
243
244         if (end_of_acl < (char *)pdacl + le16toh(pdacl->size))
245                 return;
246
247         acl_base = (char *)pdacl;
248         acl_size = sizeof(struct cifs_ctrl_acl);
249
250         num_aces = le32toh(pdacl->num_aces);
251         if (num_aces  > 0) {
252                 for (i = 0; i < num_aces; ++i) {
253                         pace = (struct cifs_ace *) (acl_base + acl_size);
254                         print_ace(pace, end_of_acl, raw);
255                         acl_base = (char *)pace;
256                         acl_size = le16toh(pace->size);
257                 }
258         }
259
260         return;
261 }
262
263 static int
264 parse_sid(struct wbcDomainSid *psid, char *end_of_acl, char *title, int raw)
265 {
266         if (end_of_acl < (char *)psid + 8)
267                 return -EINVAL;
268
269         if (title)
270                 printf("%s:", title);
271         print_sid((struct wbcDomainSid *)psid, raw);
272         printf("\n");
273
274         return 0;
275 }
276
277 static int
278 parse_sec_desc(struct cifs_ntsd *pntsd, ssize_t acl_len, int raw)
279 {
280         int rc;
281         uint32_t dacloffset;
282         char *end_of_acl = ((char *)pntsd) + acl_len;
283         struct wbcDomainSid *owner_sid_ptr, *group_sid_ptr;
284         struct cifs_ctrl_acl *dacl_ptr; /* no need for SACL ptr */
285
286         if (pntsd == NULL)
287                 return -EIO;
288
289         owner_sid_ptr = (struct wbcDomainSid *)((char *)pntsd +
290                                 le32toh(pntsd->osidoffset));
291         group_sid_ptr = (struct wbcDomainSid *)((char *)pntsd +
292                                 le32toh(pntsd->gsidoffset));
293         dacloffset = le32toh(pntsd->dacloffset);
294         dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
295         printf("REVISION:0x%x\n", pntsd->revision);
296         printf("CONTROL:0x%x\n", pntsd->type);
297
298         rc = parse_sid(owner_sid_ptr, end_of_acl, "OWNER", raw);
299         if (rc)
300                 return rc;
301
302         rc = parse_sid(group_sid_ptr, end_of_acl, "GROUP", raw);
303         if (rc)
304                 return rc;
305
306         if (dacloffset)
307                 parse_dacl(dacl_ptr, end_of_acl, raw);
308         else
309                 printf("No ACL\n"); /* BB grant all or default perms? */
310
311         return 0;
312 }
313
314 static void
315 getcifsacl_usage(void)
316 {
317         fprintf(stderr,
318         "%s: Display CIFS/NTFS ACL in a security descriptor of a file object\n",
319                 prog);
320         fprintf(stderr, "Usage: %s [option] <file_name>\n", prog);
321         fprintf(stderr, "Valid options:\n");
322         fprintf(stderr, "\t-v   Version of the program\n");
323         fprintf(stderr, "\n");
324         fprintf(stderr, "\t-r   Display raw values of the ACE fields\n");
325         fprintf(stderr, "\nRefer to getcifsacl(1) manpage for details\n");
326 }
327
328 int
329 main(const int argc, char *const argv[])
330 {
331         int c, raw = 0;
332         ssize_t attrlen;
333         size_t bufsize = BUFSIZE;
334         char *filename, *attrval;
335
336         openlog(prog, 0, LOG_DAEMON);
337
338         while ((c = getopt_long(argc, argv, "r:v", NULL, NULL)) != -1) {
339                 switch (c) {
340                 case 'v':
341                         printf("Version: %s\n", VERSION);
342                         goto out;
343                 case 'r':
344                         raw = 1;
345                         break;
346                 default:
347                         break;
348                 }
349         }
350
351         if (raw && argc == 3)
352                 filename = argv[2];
353         else if (argc == 2)
354                 filename = argv[1];
355         else {
356                 getcifsacl_usage();
357                 return 0;
358         }
359
360 cifsacl:
361         if (bufsize >= XATTR_SIZE_MAX) {
362                 printf("buffer to allocate exceeds max size of %d\n",
363                                 XATTR_SIZE_MAX);
364                 return -1;
365         }
366
367         attrval = malloc(bufsize * sizeof(char));
368         if (!attrval) {
369                 printf("error allocating memory for attribute value buffer\n");
370                 return -1;
371         }
372
373         attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
374         if (attrlen == -1) {
375                 if (errno == ERANGE) {
376                         free(attrval);
377                         bufsize += BUFSIZE;
378                         goto cifsacl;
379                 } else
380                         printf("getxattr error: %d\n", errno);
381         }
382
383         parse_sec_desc((struct cifs_ntsd *)attrval, attrlen, raw);
384
385         free(attrval);
386
387 out:
388         return 0;
389 }