1/* 2 * Unix SMB/CIFS implementation. 3 * 4 * default privileges backend for passdb 5 * 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. 12 * 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. 17 * 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. 21 */ 22 23#include "includes.h" 24 25/* 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. 30 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. 35 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. 44*/ 45 46/* 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. 51*/ 52 53 54/* 15 seconds seems like an ample time for timeouts on the privileges db */ 55#define LOCK_TIMEOUT 15 56 57 58/* the tdb handle for the privileges database */ 59static TDB_CONTEXT *tdb; 60 61 62/* initialise the privilege database */ 63BOOL privilege_init(void) 64{ 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; 70 } 71 72 return True; 73} 74 75/* 76 lock the record for a particular privilege (write lock) 77*/ 78static NTSTATUS privilege_lock_right(const char *right) 79{ 80 if (tdb_lock_bystring(tdb, right, LOCK_TIMEOUT) != 0) { 81 return NT_STATUS_INTERNAL_ERROR; 82 } 83 return NT_STATUS_OK; 84} 85 86/* 87 unlock the record for a particular privilege (write lock) 88*/ 89static void privilege_unlock_right(const char *right) 90{ 91 tdb_unlock_bystring(tdb, right); 92} 93 94 95/* 96 return a list of SIDs that have a particular right 97*/ 98NTSTATUS privilege_enum_account_with_right(const char *right, 99 uint32 *count, 100 DOM_SID **sids) 101{ 102 TDB_DATA data; 103 char *p; 104 int i; 105 106 if (!tdb) { 107 return NT_STATUS_INTERNAL_ERROR; 108 } 109 110 data = tdb_fetch_bystring(tdb, right); 111 if (!data.dptr) { 112 *count = 0; 113 *sids = NULL; 114 return NT_STATUS_OK; 115 } 116 117 /* count them */ 118 for (i=0, p=data.dptr; p<data.dptr+data.dsize; i++) { 119 p += strlen(p) + 1; 120 } 121 *count = i; 122 123 /* allocate and parse */ 124 *sids = malloc(sizeof(DOM_SID) * *count); 125 if (! *sids) { 126 return NT_STATUS_NO_MEMORY; 127 } 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; 132 } 133 p += strlen(p) + 1; 134 } 135 136 free(data.dptr); 137 138 return NT_STATUS_OK; 139} 140 141/* 142 set what accounts have a given right - this is an internal interface 143*/ 144static NTSTATUS privilege_set_accounts_with_right(const char *right, 145 uint32 count, 146 DOM_SID *sids) 147{ 148 TDB_DATA data; 149 char *p; 150 int i; 151 152 if (!tdb) { 153 return NT_STATUS_INTERNAL_ERROR; 154 } 155 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; 160 } 161 162 p = data.dptr; 163 164 for (i=0;i<count;i++) { 165 sid_to_string(p, &sids[i]); 166 p += strlen(p) + 1; 167 } 168 169 data.dsize = PTR_DIFF(p, data.dptr); 170 171 if (tdb_store_bystring(tdb, right, data, TDB_REPLACE) != 0) { 172 free(data.dptr); 173 return NT_STATUS_INTERNAL_ERROR; 174 } 175 176 free(data.dptr); 177 return NT_STATUS_OK; 178} 179 180 181/* 182 add a SID to the list of SIDs for a right 183*/ 184NTSTATUS privilege_add_account_right(const char *right, 185 DOM_SID *sid) 186{ 187 NTSTATUS status; 188 DOM_SID *current_sids; 189 uint32 current_count; 190 int i; 191 192 status = privilege_lock_right(right); 193 if (!NT_STATUS_IS_OK(status)) { 194 return status; 195 } 196 197 status = privilege_enum_account_with_right(right, ¤t_count, ¤t_sids); 198 if (!NT_STATUS_IS_OK(status)) { 199 privilege_unlock_right(right); 200 return status; 201 } 202 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); 207 free(current_sids); 208 return NT_STATUS_OK; 209 } 210 } 211 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; 217 } 218 219 sid_copy(¤t_sids[current_count], sid); 220 current_count++; 221 222 status = privilege_set_accounts_with_right(right, current_count, current_sids); 223 224 free(current_sids); 225 privilege_unlock_right(right); 226 227 return status; 228} 229 230 231/* 232 remove a SID from the list of SIDs for a right 233*/ 234NTSTATUS privilege_remove_account_right(const char *right, 235 DOM_SID *sid) 236{ 237 NTSTATUS status; 238 DOM_SID *current_sids; 239 uint32 current_count; 240 int i; 241 242 status = privilege_lock_right(right); 243 if (!NT_STATUS_IS_OK(status)) { 244 return status; 245 } 246 247 status = privilege_enum_account_with_right(right, ¤t_count, ¤t_sids); 248 if (!NT_STATUS_IS_OK(status)) { 249 privilege_unlock_right(right); 250 return status; 251 } 252 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)); 259 } 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; 267 } 268 } 269 270 /* removing a right that you don't have is not an error */ 271 272 safe_free(current_sids); 273 privilege_unlock_right(right); 274 return NT_STATUS_OK; 275} 276 277 278/* 279 an internal function for checking if a SID has a right 280*/ 281static BOOL privilege_sid_has_right(DOM_SID *sid, const char *right) 282{ 283 NTSTATUS status; 284 uint32 count; 285 DOM_SID *sids; 286 int i; 287 288 status = privilege_enum_account_with_right(right, &count, &sids); 289 if (!NT_STATUS_IS_OK(status)) { 290 return False; 291 } 292 for (i=0;i<count;i++) { 293 if (sid_equal(sid, &sids[i])) { 294 free(sids); 295 return True; 296 } 297 } 298 299 safe_free(sids); 300 return False; 301} 302 303/* 304 list the rights for an account. This involves traversing the database 305*/ 306NTSTATUS privilege_enum_account_rights(DOM_SID *sid, 307 uint32 *count, 308 char ***rights) 309{ 310 TDB_DATA key, nextkey; 311 char *right; 312 313 if (!tdb) { 314 return NT_STATUS_INTERNAL_ERROR; 315 } 316 317 *rights = NULL; 318 *count = 0; 319 320 for (key = tdb_firstkey(tdb); key.dptr; key = nextkey) { 321 nextkey = tdb_nextkey(tdb, key); 322 323 right = key.dptr; 324 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; 331 } 332 333 (*rights)[*count] = strdup(right); 334 (*count)++; 335 } 336 337 free(key.dptr); 338 } 339 340 return NT_STATUS_OK; 341} 342