r560: Fix bugzilla 1279: cannot control individual print jobs using cups
[Samba/gebeck_regimport.git] / source3 / passdb / privileges.c
blob624817e32e0285364b8e90c9d28bdab9a55bb69d
1 /*
2 * Unix SMB/CIFS implementation.
4 * default privileges backend for passdb
6 * Copyright (C) Andrew Tridgell 2003
7 *
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2 of the License, or (at your option)
11 * any later version.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
18 * You should have received a copy of the GNU General Public License along with
19 * this program; if not, write to the Free Software Foundation, Inc., 675
20 * Mass Ave, Cambridge, MA 02139, USA.
23 #include "includes.h"
26 this is a local implementation of a privileges backend, with
27 privileges stored in a tdb. Most passdb implementations will
28 probably use this backend, although some (such as pdb_ldap) will
29 store the privileges in another manner.
31 The basic principle is that the backend should store a list of SIDs
32 associated with each right, where a right is a string name such as
33 'SeTakeOwnershipPrivilege'. The SIDs can be of any type, and do not
34 need to belong to the local domain.
36 The way this is used is that certain places in the code which
37 require access control will ask the privileges backend 'does this
38 user have the following privilege'. The 'user' will be a NT_TOKEN,
39 which is essentially just a list of SIDs. If any of those SIDs are
40 listed in the list of SIDs for that privilege then the answer will
41 be 'yes'. That will usually mean that the user gets unconditional
42 access to that functionality, regradless of any ACLs. In this way
43 privileges act in a similar fashion to unix setuid bits.
47 The terms 'right' and 'privilege' are used interchangably in this
48 file. This follows MSDN convention where the LSA calls are calls on
49 'rights', which really means privileges. My apologies for the
50 confusion.
54 /* 15 seconds seems like an ample time for timeouts on the privileges db */
55 #define LOCK_TIMEOUT 15
58 /* the tdb handle for the privileges database */
59 static TDB_CONTEXT *tdb;
62 /* initialise the privilege database */
63 BOOL privilege_init(void)
65 tdb = tdb_open_log(lock_path("privilege.tdb"), 0, TDB_DEFAULT,
66 O_RDWR|O_CREAT, 0600);
67 if (!tdb) {
68 DEBUG(0,("Failed to open privilege database\n"));
69 return False;
72 return True;
75 /*
76 lock the record for a particular privilege (write lock)
78 static NTSTATUS privilege_lock_right(const char *right)
80 if (tdb_lock_bystring(tdb, right, LOCK_TIMEOUT) != 0) {
81 return NT_STATUS_INTERNAL_ERROR;
83 return NT_STATUS_OK;
86 /*
87 unlock the record for a particular privilege (write lock)
89 static void privilege_unlock_right(const char *right)
91 tdb_unlock_bystring(tdb, right);
95 /*
96 return a list of SIDs that have a particular right
98 NTSTATUS privilege_enum_account_with_right(const char *right,
99 uint32 *count,
100 DOM_SID **sids)
102 TDB_DATA data;
103 char *p;
104 int i;
106 if (!tdb) {
107 return NT_STATUS_INTERNAL_ERROR;
110 data = tdb_fetch_bystring(tdb, right);
111 if (!data.dptr) {
112 *count = 0;
113 *sids = NULL;
114 return NT_STATUS_OK;
117 /* count them */
118 for (i=0, p=data.dptr; p<data.dptr+data.dsize; i++) {
119 p += strlen(p) + 1;
121 *count = i;
123 /* allocate and parse */
124 *sids = malloc(sizeof(DOM_SID) * *count);
125 if (! *sids) {
126 return NT_STATUS_NO_MEMORY;
128 for (i=0, p=data.dptr; p<data.dptr+data.dsize; i++) {
129 if (!string_to_sid(&(*sids)[i], p)) {
130 free(data.dptr);
131 return NT_STATUS_INTERNAL_DB_CORRUPTION;
133 p += strlen(p) + 1;
136 free(data.dptr);
138 return NT_STATUS_OK;
142 set what accounts have a given right - this is an internal interface
144 static NTSTATUS privilege_set_accounts_with_right(const char *right,
145 uint32 count,
146 DOM_SID *sids)
148 TDB_DATA data;
149 char *p;
150 int i;
152 if (!tdb) {
153 return NT_STATUS_INTERNAL_ERROR;
156 /* allocate the maximum size that we might use */
157 data.dptr = malloc(count * ((MAXSUBAUTHS*11) + 30));
158 if (!data.dptr) {
159 return NT_STATUS_NO_MEMORY;
162 p = data.dptr;
164 for (i=0;i<count;i++) {
165 sid_to_string(p, &sids[i]);
166 p += strlen(p) + 1;
169 data.dsize = PTR_DIFF(p, data.dptr);
171 if (tdb_store_bystring(tdb, right, data, TDB_REPLACE) != 0) {
172 free(data.dptr);
173 return NT_STATUS_INTERNAL_ERROR;
176 free(data.dptr);
177 return NT_STATUS_OK;
182 add a SID to the list of SIDs for a right
184 NTSTATUS privilege_add_account_right(const char *right,
185 DOM_SID *sid)
187 NTSTATUS status;
188 DOM_SID *current_sids;
189 uint32 current_count;
190 int i;
192 status = privilege_lock_right(right);
193 if (!NT_STATUS_IS_OK(status)) {
194 return status;
197 status = privilege_enum_account_with_right(right, &current_count, &current_sids);
198 if (!NT_STATUS_IS_OK(status)) {
199 privilege_unlock_right(right);
200 return status;
203 /* maybe that SID is already listed? this is not an error */
204 for (i=0;i<current_count;i++) {
205 if (sid_equal(&current_sids[i], sid)) {
206 privilege_unlock_right(right);
207 free(current_sids);
208 return NT_STATUS_OK;
212 /* add it in */
213 current_sids = Realloc(current_sids, sizeof(current_sids[0]) * (current_count+1));
214 if (!current_sids) {
215 privilege_unlock_right(right);
216 return NT_STATUS_NO_MEMORY;
219 sid_copy(&current_sids[current_count], sid);
220 current_count++;
222 status = privilege_set_accounts_with_right(right, current_count, current_sids);
224 free(current_sids);
225 privilege_unlock_right(right);
227 return status;
232 remove a SID from the list of SIDs for a right
234 NTSTATUS privilege_remove_account_right(const char *right,
235 DOM_SID *sid)
237 NTSTATUS status;
238 DOM_SID *current_sids;
239 uint32 current_count;
240 int i;
242 status = privilege_lock_right(right);
243 if (!NT_STATUS_IS_OK(status)) {
244 return status;
247 status = privilege_enum_account_with_right(right, &current_count, &current_sids);
248 if (!NT_STATUS_IS_OK(status)) {
249 privilege_unlock_right(right);
250 return status;
253 for (i=0;i<current_count;i++) {
254 if (sid_equal(&current_sids[i], sid)) {
255 /* found it - so remove it */
256 if (current_count-i > 1) {
257 memmove(&current_sids[i], &current_sids[i+1],
258 sizeof(current_sids[0]) * ((current_count-i)-1));
260 current_count--;
261 status = privilege_set_accounts_with_right(right,
262 current_count,
263 current_sids);
264 free(current_sids);
265 privilege_unlock_right(right);
266 return status;
270 /* removing a right that you don't have is not an error */
272 safe_free(current_sids);
273 privilege_unlock_right(right);
274 return NT_STATUS_OK;
279 an internal function for checking if a SID has a right
281 static BOOL privilege_sid_has_right(DOM_SID *sid, const char *right)
283 NTSTATUS status;
284 uint32 count;
285 DOM_SID *sids;
286 int i;
288 status = privilege_enum_account_with_right(right, &count, &sids);
289 if (!NT_STATUS_IS_OK(status)) {
290 return False;
292 for (i=0;i<count;i++) {
293 if (sid_equal(sid, &sids[i])) {
294 free(sids);
295 return True;
299 safe_free(sids);
300 return False;
304 list the rights for an account. This involves traversing the database
306 NTSTATUS privilege_enum_account_rights(DOM_SID *sid,
307 uint32 *count,
308 char ***rights)
310 TDB_DATA key, nextkey;
311 char *right;
313 if (!tdb) {
314 return NT_STATUS_INTERNAL_ERROR;
317 *rights = NULL;
318 *count = 0;
320 for (key = tdb_firstkey(tdb); key.dptr; key = nextkey) {
321 nextkey = tdb_nextkey(tdb, key);
323 right = key.dptr;
325 if (privilege_sid_has_right(sid, right)) {
326 (*rights) = (char **)Realloc(*rights,sizeof(char *) * ((*count)+1));
327 if (! *rights) {
328 safe_free(nextkey.dptr);
329 free(key.dptr);
330 return NT_STATUS_NO_MEMORY;
333 (*rights)[*count] = strdup(right);
334 (*count)++;
337 free(key.dptr);
340 return NT_STATUS_OK;