]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/acl.c
Implement ACL removal. That was the last piece, POSIX 1e ACL support is finished...
[netatalk.git] / libatalk / vfs / acl.c
1 /*
2   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
3   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 */
15
16 #ifdef HAVE_CONFIG_H
17 #include "config.h"
18 #endif /* HAVE_CONFIG_H */
19
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26
27 #include <atalk/afp.h>
28 #include <atalk/util.h>
29 #include <atalk/logger.h>
30 #include <atalk/errchk.h>
31 #include <atalk/acl.h>
32
33 #ifdef HAVE_SOLARIS_ACLS
34
35 /* Get ACL. Allocates storage as needed. Caller must free.
36  * Returns no of ACEs or -1 on error.  */
37 int get_nfsv4_acl(const char *name, ace_t **retAces)
38 {
39     int ace_count = -1;
40     ace_t *aces;
41     struct stat st;
42
43     *retAces = NULL;
44     /* Only call acl() for regular files and directories, otherwise just return 0 */
45     if (lstat(name, &st) != 0)
46         return -1;
47     if ( ! (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)))
48         return 0;
49     if ((ace_count = acl(name, ACE_GETACLCNT, 0, NULL)) == 0)
50         return 0;
51
52     if (ace_count == -1) {
53         LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl('%s/%s', ACE_GETACLCNT): ace_count %i, error: %s",
54             getcwdpath(), name, ace_count, strerror(errno));
55         return -1;
56     }
57
58     aces = malloc(ace_count * sizeof(ace_t));
59     if (aces == NULL) {
60         LOG(log_error, logtype_afpd, "get_nfsv4_acl: malloc error");
61         return -1;
62     }
63
64     if ( (acl(name, ACE_GETACL, ace_count, aces)) == -1 ) {
65         LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACL) error");
66         free(aces);
67         return -1;
68     }
69
70     LOG(log_debug9, logtype_afpd, "get_nfsv4_acl: file: %s -> No. of ACEs: %d", name, ace_count);
71     *retAces = aces;
72
73     return ace_count;
74 }
75
76 /* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
77 int remove_acl_vfs(const char *name)
78 {
79     int ret,i, ace_count, trivial_aces, new_aces_count;
80     ace_t *old_aces = NULL;
81     ace_t *new_aces = NULL;
82
83     LOG(log_debug9, logtype_afpd, "remove_acl: BEGIN");
84
85     /* Get existing ACL and count trivial ACEs */
86     if ((ace_count = get_nfsv4_acl(name, &old_aces)) == -1)
87         return AFPERR_MISC;
88     trivial_aces = 0;
89     for ( i=0; i < ace_count; i++) {
90         if (old_aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE))
91             trivial_aces++;
92     }
93
94     /* malloc buffer for new ACL */
95     if ((new_aces = malloc(trivial_aces * sizeof(ace_t))) == NULL) {
96         LOG(log_error, logtype_afpd, "remove_acl: malloc %s", strerror(errno));
97         ret = AFPERR_MISC;
98         goto exit;
99     }
100
101     /* Now copy the trivial ACEs */
102     new_aces_count = 0;
103     for (i=0; i < ace_count; i++) {
104         if (old_aces[i].a_flags  & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
105             memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
106             new_aces_count++;
107         }
108     }
109
110     if ( (acl(name, ACE_SETACL, trivial_aces, new_aces)) == 0)
111         ret = AFP_OK;
112     else {
113         LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
114         if (errno == (EACCES | EPERM))
115             ret = AFPERR_ACCESS;
116         else if (errno == ENOENT)
117             ret = AFPERR_NOITEM;
118         else
119             ret = AFPERR_MISC;
120     }
121
122 exit:
123     free(old_aces);
124     free(new_aces);
125
126     LOG(log_debug9, logtype_afpd, "remove_acl: END");
127     return ret;
128 }
129
130 #endif  /* HAVE_SOLARIS_ACLS */
131
132 #ifdef HAVE_POSIX_ACLS
133 /*!
134  * Remove any ACL_USER, ACL_GROUP or ACL_TYPE_DEFAULT ACEs from an object
135  *
136  * @param name  (r) filesystem object name
137  *
138  * @returns AFP error code, AFP_OK (= 0) on success, AFPERR_MISC on error
139  */
140 int remove_acl_vfs(const char *name)
141 {
142     EC_INIT;
143
144     struct stat st;
145     acl_t acl = NULL;
146     acl_entry_t e;
147     acl_tag_t tag;
148     int entry_id = ACL_FIRST_ENTRY;
149
150
151     /* Remove default ACL if it's a dir */
152     EC_ZERO_LOG_ERR(stat(name, &st), AFPERR_MISC);
153     if (S_ISDIR(st.st_mode)) {
154         EC_NULL_LOG_ERR(acl = acl_init(0), AFPERR_MISC);
155         EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_DEFAULT, acl), AFPERR_MISC);
156         EC_ZERO_LOG_ERR(acl_free(acl), AFPERR_MISC);
157         acl = NULL;
158     }
159
160     /* Now get ACL and remove ACL_USER or ACL_GROUP entries, then re-set the ACL again */
161     EC_NULL_LOG_ERR(acl = acl_get_file(name, ACL_TYPE_ACCESS), AFPERR_MISC);
162     for ( ; acl_get_entry(acl, entry_id, &e) == 1; entry_id = ACL_NEXT_ENTRY) {
163         EC_ZERO_LOG_ERR(acl_get_tag_type(e, &tag), AFPERR_MISC);
164         if (tag == ACL_USER || tag == ACL_GROUP)
165             EC_ZERO_LOG_ERR(acl_delete_entry(acl, e), AFPERR_MISC);
166     }
167     EC_ZERO_LOG_ERR(acl_calc_mask(&acl), AFPERR_MISC);
168     EC_ZERO_LOG_ERR(acl_valid(acl), AFPERR_MISC);
169     EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_ACCESS, acl), AFPERR_MISC);
170
171 EC_CLEANUP:
172     if (acl) acl_free(acl);
173
174     EC_EXIT;
175 }
176 #endif /* HAVE_POSIX_ACLS */