2 * Unix SMB/CIFS implementation.
4 * default privileges backend for passdb
6 * Copyright (C) Andrew Tridgell 2003
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)
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
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.
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
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);
68 DEBUG(0,("Failed to open privilege database\n"));
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
;
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
);
96 return a list of SIDs that have a particular right
98 NTSTATUS
privilege_enum_account_with_right(const char *right
,
107 return NT_STATUS_INTERNAL_ERROR
;
110 data
= tdb_fetch_bystring(tdb
, right
);
118 for (i
=0, p
=data
.dptr
; p
<data
.dptr
+data
.dsize
; i
++) {
123 /* allocate and parse */
124 *sids
= malloc(sizeof(DOM_SID
) * *count
);
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
)) {
131 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
142 set what accounts have a given right - this is an internal interface
144 static NTSTATUS
privilege_set_accounts_with_right(const char *right
,
153 return NT_STATUS_INTERNAL_ERROR
;
156 /* allocate the maximum size that we might use */
157 data
.dptr
= malloc(count
* ((MAXSUBAUTHS
*11) + 30));
159 return NT_STATUS_NO_MEMORY
;
164 for (i
=0;i
<count
;i
++) {
165 sid_to_string(p
, &sids
[i
]);
169 data
.dsize
= PTR_DIFF(p
, data
.dptr
);
171 if (tdb_store_bystring(tdb
, right
, data
, TDB_REPLACE
) != 0) {
173 return NT_STATUS_INTERNAL_ERROR
;
182 add a SID to the list of SIDs for a right
184 NTSTATUS
privilege_add_account_right(const char *right
,
188 DOM_SID
*current_sids
;
189 uint32 current_count
;
192 status
= privilege_lock_right(right
);
193 if (!NT_STATUS_IS_OK(status
)) {
197 status
= privilege_enum_account_with_right(right
, ¤t_count
, ¤t_sids
);
198 if (!NT_STATUS_IS_OK(status
)) {
199 privilege_unlock_right(right
);
203 /* maybe that SID is already listed? this is not an error */
204 for (i
=0;i
<current_count
;i
++) {
205 if (sid_equal(¤t_sids
[i
], sid
)) {
206 privilege_unlock_right(right
);
213 current_sids
= Realloc(current_sids
, sizeof(current_sids
[0]) * (current_count
+1));
215 privilege_unlock_right(right
);
216 return NT_STATUS_NO_MEMORY
;
219 sid_copy(¤t_sids
[current_count
], sid
);
222 status
= privilege_set_accounts_with_right(right
, current_count
, current_sids
);
225 privilege_unlock_right(right
);
232 remove a SID from the list of SIDs for a right
234 NTSTATUS
privilege_remove_account_right(const char *right
,
238 DOM_SID
*current_sids
;
239 uint32 current_count
;
242 status
= privilege_lock_right(right
);
243 if (!NT_STATUS_IS_OK(status
)) {
247 status
= privilege_enum_account_with_right(right
, ¤t_count
, ¤t_sids
);
248 if (!NT_STATUS_IS_OK(status
)) {
249 privilege_unlock_right(right
);
253 for (i
=0;i
<current_count
;i
++) {
254 if (sid_equal(¤t_sids
[i
], sid
)) {
255 /* found it - so remove it */
256 if (current_count
-i
> 1) {
257 memmove(¤t_sids
[i
], ¤t_sids
[i
+1],
258 sizeof(current_sids
[0]) * ((current_count
-i
)-1));
261 status
= privilege_set_accounts_with_right(right
,
265 privilege_unlock_right(right
);
270 /* removing a right that you don't have is not an error */
272 safe_free(current_sids
);
273 privilege_unlock_right(right
);
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
)
288 status
= privilege_enum_account_with_right(right
, &count
, &sids
);
289 if (!NT_STATUS_IS_OK(status
)) {
292 for (i
=0;i
<count
;i
++) {
293 if (sid_equal(sid
, &sids
[i
])) {
304 list the rights for an account. This involves traversing the database
306 NTSTATUS
privilege_enum_account_rights(DOM_SID
*sid
,
310 TDB_DATA key
, nextkey
;
314 return NT_STATUS_INTERNAL_ERROR
;
320 for (key
= tdb_firstkey(tdb
); key
.dptr
; key
= nextkey
) {
321 nextkey
= tdb_nextkey(tdb
, key
);
325 if (privilege_sid_has_right(sid
, right
)) {
326 (*rights
) = (char **)Realloc(*rights
,sizeof(char *) * ((*count
)+1));
328 safe_free(nextkey
.dptr
);
330 return NT_STATUS_NO_MEMORY
;
333 (*rights
)[*count
] = strdup(right
);