/src/samba/source3/lib/privileges.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | Privileges handling functions |
4 | | Copyright (C) Jean François Micouleau 1998-2001 |
5 | | Copyright (C) Simo Sorce 2002-2003 |
6 | | Copyright (C) Gerald (Jerry) Carter 2005 |
7 | | Copyright (C) Michael Adam 2007 |
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 3 of the License, or |
12 | | (at your option) any later version. |
13 | | |
14 | | This program is distributed in the hope that it will be useful, |
15 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | GNU General Public License for more details. |
18 | | |
19 | | You should have received a copy of the GNU General Public License |
20 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | |
24 | | #include "includes.h" |
25 | | #include "lib/privileges.h" |
26 | | #include "dbwrap/dbwrap.h" |
27 | | #include "libcli/security/privileges_private.h" |
28 | | #include "../libcli/security/security.h" |
29 | | #include "passdb.h" |
30 | | #include "lib/util/string_wrappers.h" |
31 | | |
32 | 0 | #define PRIVPREFIX "PRIV_" |
33 | | |
34 | | typedef struct { |
35 | | uint32_t count; |
36 | | struct dom_sid *list; |
37 | | } SID_LIST; |
38 | | |
39 | | typedef struct { |
40 | | TALLOC_CTX *mem_ctx; |
41 | | uint64_t privilege; |
42 | | SID_LIST sids; |
43 | | } PRIV_SID_LIST; |
44 | | |
45 | | /* |
46 | | interpret an old style SE_PRIV structure |
47 | | */ |
48 | | static uint64_t map_old_SE_PRIV(unsigned char *dptr) |
49 | 0 | { |
50 | 0 | uint32_t *old_masks = (uint32_t *)dptr; |
51 | | /* |
52 | | * the old privileges code only ever used up to 0x800, except |
53 | | * for a special case of 'SE_ALL_PRIVS' which was 0xFFFFFFFF |
54 | | */ |
55 | 0 | if (old_masks[0] == 0xFFFFFFFF) { |
56 | | /* they set all privileges */ |
57 | 0 | return SE_ALL_PRIVS; |
58 | 0 | } |
59 | | |
60 | | /* the old code used the machine byte order, but we don't know |
61 | | * the byte order of the machine that wrote it. However we can |
62 | | * tell what byte order it was by taking advantage of the fact |
63 | | * that it only ever use up to 0x800 |
64 | | */ |
65 | 0 | if (dptr[0] || dptr[1]) { |
66 | | /* it was little endian */ |
67 | 0 | return IVAL(dptr, 0); |
68 | 0 | } |
69 | | |
70 | | /* it was either zero or big-endian */ |
71 | 0 | return RIVAL(dptr, 0); |
72 | 0 | } |
73 | | |
74 | | |
75 | | static bool get_privileges( const struct dom_sid *sid, uint64_t *mask ) |
76 | 0 | { |
77 | 0 | struct db_context *db = get_account_pol_db(); |
78 | 0 | struct dom_sid_buf tmp; |
79 | 0 | fstring keystr; |
80 | 0 | TDB_DATA data; |
81 | 0 | NTSTATUS status; |
82 | | |
83 | | /* Fail if the admin has not enable privileges */ |
84 | |
|
85 | 0 | if ( !lp_enable_privileges() ) { |
86 | 0 | return False; |
87 | 0 | } |
88 | | |
89 | 0 | if ( db == NULL ) |
90 | 0 | return False; |
91 | | |
92 | | /* PRIV_<SID> (NULL terminated) as the key */ |
93 | | |
94 | 0 | fstr_sprintf(keystr, "%s%s", PRIVPREFIX, dom_sid_str_buf(sid, &tmp)); |
95 | |
|
96 | 0 | status = dbwrap_fetch_bystring(db, talloc_tos(), keystr, &data); |
97 | |
|
98 | 0 | if (!NT_STATUS_IS_OK(status)) { |
99 | 0 | DEBUG(4, ("get_privileges: No privileges assigned to SID " |
100 | 0 | "[%s]\n", tmp.buf)); |
101 | 0 | return False; |
102 | 0 | } |
103 | | |
104 | 0 | if (data.dsize == 4*4) { |
105 | | /* it's an old style SE_PRIV structure. */ |
106 | 0 | *mask = map_old_SE_PRIV(data.dptr); |
107 | 0 | } else { |
108 | 0 | if (data.dsize != sizeof( uint64_t ) ) { |
109 | 0 | DEBUG(3, ("get_privileges: Invalid privileges record assigned to SID " |
110 | 0 | "[%s]\n", tmp.buf)); |
111 | 0 | return False; |
112 | 0 | } |
113 | | |
114 | 0 | *mask = BVAL(data.dptr, 0); |
115 | 0 | } |
116 | | |
117 | 0 | TALLOC_FREE(data.dptr); |
118 | |
|
119 | 0 | return True; |
120 | 0 | } |
121 | | |
122 | | /*************************************************************************** |
123 | | Store the privilege mask (set) for a given SID |
124 | | ****************************************************************************/ |
125 | | |
126 | | static bool set_privileges( const struct dom_sid *sid, uint64_t mask ) |
127 | 0 | { |
128 | 0 | struct db_context *db = get_account_pol_db(); |
129 | 0 | uint8_t privbuf[8]; |
130 | 0 | struct dom_sid_buf tmp; |
131 | 0 | fstring keystr; |
132 | 0 | TDB_DATA data = { .dptr = privbuf, .dsize = sizeof(privbuf), }; |
133 | |
|
134 | 0 | if ( !lp_enable_privileges() ) |
135 | 0 | return False; |
136 | | |
137 | 0 | if ( db == NULL ) |
138 | 0 | return False; |
139 | | |
140 | 0 | if ( !sid || (sid->num_auths == 0) ) { |
141 | 0 | DEBUG(0,("set_privileges: Refusing to store empty SID!\n")); |
142 | 0 | return False; |
143 | 0 | } |
144 | | |
145 | | /* PRIV_<SID> (NULL terminated) as the key */ |
146 | | |
147 | 0 | fstr_sprintf(keystr, "%s%s", PRIVPREFIX, dom_sid_str_buf(sid, &tmp)); |
148 | | |
149 | | /* This writes the 64 bit bitmask out in little endian format */ |
150 | 0 | SBVAL(privbuf,0,mask); |
151 | |
|
152 | 0 | return NT_STATUS_IS_OK(dbwrap_store_bystring(db, keystr, data, |
153 | 0 | TDB_REPLACE)); |
154 | 0 | } |
155 | | |
156 | | /********************************************************************* |
157 | | get a list of all privileges for all sids in the list |
158 | | *********************************************************************/ |
159 | | |
160 | | bool get_privileges_for_sids(uint64_t *privileges, struct dom_sid *slist, int scount) |
161 | 0 | { |
162 | 0 | uint64_t mask; |
163 | 0 | int i; |
164 | 0 | bool found = False; |
165 | |
|
166 | 0 | *privileges = 0; |
167 | |
|
168 | 0 | for ( i=0; i<scount; i++ ) { |
169 | 0 | struct dom_sid_buf buf; |
170 | | |
171 | | /* don't add unless we actually have a privilege assigned */ |
172 | |
|
173 | 0 | if ( !get_privileges( &slist[i], &mask ) ) |
174 | 0 | continue; |
175 | | |
176 | 0 | DBG_INFO("sid = %s\nPrivilege set: 0x%"PRIx64"\n", |
177 | 0 | dom_sid_str_buf(&slist[i], &buf), |
178 | 0 | mask); |
179 | |
|
180 | 0 | *privileges |= mask; |
181 | 0 | found = True; |
182 | 0 | } |
183 | |
|
184 | 0 | return found; |
185 | 0 | } |
186 | | |
187 | | NTSTATUS get_privileges_for_sid_as_set(TALLOC_CTX *mem_ctx, PRIVILEGE_SET **privileges, struct dom_sid *sid) |
188 | 0 | { |
189 | 0 | uint64_t mask; |
190 | 0 | if (!get_privileges(sid, &mask)) { |
191 | 0 | return NT_STATUS_OBJECT_NAME_NOT_FOUND; |
192 | 0 | } |
193 | | |
194 | 0 | *privileges = talloc_zero(mem_ctx, PRIVILEGE_SET); |
195 | 0 | if (!*privileges) { |
196 | 0 | return NT_STATUS_NO_MEMORY; |
197 | 0 | } |
198 | | |
199 | 0 | if (!se_priv_to_privilege_set(*privileges, mask)) { |
200 | 0 | return NT_STATUS_NO_MEMORY; |
201 | 0 | } |
202 | 0 | return NT_STATUS_OK; |
203 | 0 | } |
204 | | |
205 | | /********************************************************************* |
206 | | traversal functions for privilege_enumerate_accounts |
207 | | *********************************************************************/ |
208 | | |
209 | | static int priv_traverse_fn(struct db_record *rec, void *state) |
210 | 0 | { |
211 | 0 | PRIV_SID_LIST *priv = (PRIV_SID_LIST *)state; |
212 | 0 | int prefixlen = strlen(PRIVPREFIX); |
213 | 0 | struct dom_sid sid; |
214 | 0 | fstring sid_string; |
215 | 0 | TDB_DATA key; |
216 | |
|
217 | 0 | key = dbwrap_record_get_key(rec); |
218 | | |
219 | | /* check we have a PRIV_+SID entry */ |
220 | |
|
221 | 0 | if (strncmp((char *)key.dptr, PRIVPREFIX, prefixlen) != 0) |
222 | 0 | return 0; |
223 | | |
224 | | /* check to see if we are looking for a particular privilege */ |
225 | | |
226 | 0 | fstrcpy( sid_string, (char *)&(key.dptr[strlen(PRIVPREFIX)]) ); |
227 | |
|
228 | 0 | if (priv->privilege != 0) { |
229 | 0 | uint64_t mask; |
230 | 0 | TDB_DATA value; |
231 | |
|
232 | 0 | value = dbwrap_record_get_value(rec); |
233 | |
|
234 | 0 | if (value.dsize == 4*4) { |
235 | 0 | mask = map_old_SE_PRIV(value.dptr); |
236 | 0 | } else { |
237 | 0 | if (value.dsize != sizeof( uint64_t ) ) { |
238 | 0 | DEBUG(3, ("get_privileges: Invalid privileges record assigned to SID " |
239 | 0 | "[%s]\n", sid_string)); |
240 | 0 | return 0; |
241 | 0 | } |
242 | 0 | mask = BVAL(value.dptr, 0); |
243 | 0 | } |
244 | | |
245 | | /* if the SID does not have the specified privilege |
246 | | then just return */ |
247 | | |
248 | 0 | if ((mask & priv->privilege) == 0) { |
249 | 0 | return 0; |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | | /* this is a last ditch safety check to preventing returning |
254 | | and invalid SID (i've somehow run into this on development branches) */ |
255 | | |
256 | 0 | if ( strcmp( "S-0-0", sid_string ) == 0 ) |
257 | 0 | return 0; |
258 | | |
259 | 0 | if ( !string_to_sid(&sid, sid_string) ) { |
260 | 0 | DBG_WARNING("Could not convert SID [%s]\n", sid_string); |
261 | 0 | return 0; |
262 | 0 | } |
263 | | |
264 | 0 | if (!NT_STATUS_IS_OK(add_sid_to_array(priv->mem_ctx, &sid, |
265 | 0 | &priv->sids.list, |
266 | 0 | &priv->sids.count))) |
267 | 0 | { |
268 | 0 | return 0; |
269 | 0 | } |
270 | | |
271 | 0 | return 0; |
272 | 0 | } |
273 | | |
274 | | /********************************************************************* |
275 | | Retrieve list of privileged SIDs (for _lsa_enumerate_accounts() |
276 | | *********************************************************************/ |
277 | | |
278 | | NTSTATUS privilege_enumerate_accounts(struct dom_sid **sids, int *num_sids) |
279 | 0 | { |
280 | 0 | struct db_context *db = get_account_pol_db(); |
281 | 0 | PRIV_SID_LIST priv; |
282 | 0 | NTSTATUS status; |
283 | |
|
284 | 0 | if (db == NULL) { |
285 | 0 | return NT_STATUS_ACCESS_DENIED; |
286 | 0 | } |
287 | | |
288 | 0 | ZERO_STRUCT(priv); |
289 | |
|
290 | 0 | status = dbwrap_traverse_read(db, priv_traverse_fn, &priv, NULL); |
291 | 0 | if (!NT_STATUS_IS_OK(status)) { |
292 | 0 | return status; |
293 | 0 | } |
294 | | |
295 | | /* give the memory away; caller will free */ |
296 | | |
297 | 0 | *sids = priv.sids.list; |
298 | 0 | *num_sids = priv.sids.count; |
299 | |
|
300 | 0 | return NT_STATUS_OK; |
301 | 0 | } |
302 | | |
303 | | /********************************************************************* |
304 | | Retrieve list of SIDs granted a particular privilege |
305 | | *********************************************************************/ |
306 | | |
307 | | NTSTATUS privilege_enum_sids(enum sec_privilege privilege, TALLOC_CTX *mem_ctx, |
308 | | struct dom_sid **sids, int *num_sids) |
309 | 0 | { |
310 | 0 | struct db_context *db = get_account_pol_db(); |
311 | 0 | PRIV_SID_LIST priv; |
312 | 0 | NTSTATUS status; |
313 | |
|
314 | 0 | if (db == NULL) { |
315 | 0 | return NT_STATUS_ACCESS_DENIED; |
316 | 0 | } |
317 | | |
318 | 0 | ZERO_STRUCT(priv); |
319 | |
|
320 | 0 | priv.privilege = sec_privilege_mask(privilege); |
321 | 0 | priv.mem_ctx = mem_ctx; |
322 | |
|
323 | 0 | status = dbwrap_traverse_read(db, priv_traverse_fn, &priv, NULL); |
324 | 0 | if (!NT_STATUS_IS_OK(status)) { |
325 | 0 | return status; |
326 | 0 | } |
327 | | |
328 | | /* give the memory away; caller will free */ |
329 | | |
330 | 0 | *sids = priv.sids.list; |
331 | 0 | *num_sids = priv.sids.count; |
332 | |
|
333 | 0 | return NT_STATUS_OK; |
334 | 0 | } |
335 | | |
336 | | /*************************************************************************** |
337 | | Add privilege to sid |
338 | | ****************************************************************************/ |
339 | | |
340 | | static bool grant_privilege_bitmap(const struct dom_sid *sid, const uint64_t priv_mask) |
341 | 0 | { |
342 | 0 | uint64_t old_mask = 0, new_mask = 0; |
343 | 0 | struct dom_sid_buf buf; |
344 | |
|
345 | 0 | if ( get_privileges( sid, &old_mask ) ) { |
346 | 0 | new_mask = old_mask; |
347 | 0 | } |
348 | |
|
349 | 0 | new_mask |= priv_mask; |
350 | |
|
351 | 0 | DBG_DEBUG("%s\n" |
352 | 0 | "original privilege mask: 0x%"PRIx64"\n" |
353 | 0 | "new privilege mask: 0x%"PRIx64"\n", |
354 | 0 | dom_sid_str_buf(sid, &buf), |
355 | 0 | old_mask, |
356 | 0 | new_mask); |
357 | |
|
358 | 0 | return set_privileges( sid, new_mask ); |
359 | 0 | } |
360 | | |
361 | | /********************************************************************* |
362 | | Add a privilege based on its name |
363 | | *********************************************************************/ |
364 | | |
365 | | bool grant_privilege_by_name(const struct dom_sid *sid, const char *name) |
366 | 0 | { |
367 | 0 | uint64_t mask; |
368 | |
|
369 | 0 | if (! se_priv_from_name(name, &mask)) { |
370 | 0 | DEBUG(3, ("grant_privilege_by_name: " |
371 | 0 | "No Such Privilege Found (%s)\n", name)); |
372 | 0 | return False; |
373 | 0 | } |
374 | | |
375 | 0 | return grant_privilege_bitmap( sid, mask ); |
376 | 0 | } |
377 | | |
378 | | /*************************************************************************** |
379 | | Grant a privilege set (list of LUID values) from a sid |
380 | | ****************************************************************************/ |
381 | | |
382 | | bool grant_privilege_set(const struct dom_sid *sid, struct lsa_PrivilegeSet *set) |
383 | 0 | { |
384 | 0 | uint64_t privilege_mask; |
385 | 0 | if (!privilege_set_to_se_priv(&privilege_mask, set)) { |
386 | 0 | return false; |
387 | 0 | } |
388 | 0 | return grant_privilege_bitmap(sid, privilege_mask); |
389 | 0 | } |
390 | | |
391 | | /*************************************************************************** |
392 | | Remove privilege from sid |
393 | | ****************************************************************************/ |
394 | | |
395 | | static bool revoke_privilege_bitmap(const struct dom_sid *sid, const uint64_t priv_mask) |
396 | 0 | { |
397 | 0 | uint64_t mask; |
398 | 0 | struct dom_sid_buf buf; |
399 | | |
400 | | /* if the user has no privileges, then we can't revoke any */ |
401 | |
|
402 | 0 | if ( !get_privileges( sid, &mask ) ) |
403 | 0 | return True; |
404 | | |
405 | 0 | DEBUG(10,("revoke_privilege: %s\n", dom_sid_str_buf(sid, &buf))); |
406 | |
|
407 | 0 | DEBUGADD( 10, ("original privilege mask: 0x%llx\n", (unsigned long long)mask)); |
408 | |
|
409 | 0 | mask &= ~priv_mask; |
410 | |
|
411 | 0 | DEBUGADD( 10, ("new privilege mask: 0x%llx\n", (unsigned long long)mask)); |
412 | |
|
413 | 0 | return set_privileges( sid, mask ); |
414 | 0 | } |
415 | | |
416 | | /*************************************************************************** |
417 | | Remove a privilege set (list of LUID values) from a sid |
418 | | ****************************************************************************/ |
419 | | |
420 | | bool revoke_privilege_set(const struct dom_sid *sid, struct lsa_PrivilegeSet *set) |
421 | 0 | { |
422 | 0 | uint64_t privilege_mask; |
423 | 0 | if (!privilege_set_to_se_priv(&privilege_mask, set)) { |
424 | 0 | return false; |
425 | 0 | } |
426 | 0 | return revoke_privilege_bitmap(sid, privilege_mask); |
427 | 0 | } |
428 | | |
429 | | /********************************************************************* |
430 | | Revoke all privileges |
431 | | *********************************************************************/ |
432 | | |
433 | | bool revoke_all_privileges( const struct dom_sid *sid ) |
434 | 0 | { |
435 | 0 | return revoke_privilege_bitmap( sid, SE_ALL_PRIVS); |
436 | 0 | } |
437 | | |
438 | | /********************************************************************* |
439 | | Add a privilege based on its name |
440 | | *********************************************************************/ |
441 | | |
442 | | bool revoke_privilege_by_name(const struct dom_sid *sid, const char *name) |
443 | 0 | { |
444 | 0 | uint64_t mask; |
445 | |
|
446 | 0 | if (! se_priv_from_name(name, &mask)) { |
447 | 0 | DEBUG(3, ("revoke_privilege_by_name: " |
448 | 0 | "No Such Privilege Found (%s)\n", name)); |
449 | 0 | return False; |
450 | 0 | } |
451 | | |
452 | 0 | return revoke_privilege_bitmap(sid, mask); |
453 | |
|
454 | 0 | } |
455 | | |
456 | | /*************************************************************************** |
457 | | Retrieve the SIDs assigned to a given privilege |
458 | | ****************************************************************************/ |
459 | | |
460 | | NTSTATUS privilege_create_account(const struct dom_sid *sid ) |
461 | 0 | { |
462 | 0 | return ( grant_privilege_bitmap(sid, 0) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL); |
463 | 0 | } |
464 | | |
465 | | /*************************************************************************** |
466 | | Delete a privileged account |
467 | | ****************************************************************************/ |
468 | | |
469 | | NTSTATUS privilege_delete_account(const struct dom_sid *sid) |
470 | 0 | { |
471 | 0 | struct db_context *db = get_account_pol_db(); |
472 | 0 | struct dom_sid_buf tmp; |
473 | 0 | fstring keystr; |
474 | |
|
475 | 0 | if (!lp_enable_privileges()) { |
476 | 0 | return NT_STATUS_OK; |
477 | 0 | } |
478 | | |
479 | 0 | if (!db) { |
480 | 0 | return NT_STATUS_INVALID_HANDLE; |
481 | 0 | } |
482 | | |
483 | 0 | if (!sid || (sid->num_auths == 0)) { |
484 | 0 | return NT_STATUS_INVALID_SID; |
485 | 0 | } |
486 | | |
487 | | /* PRIV_<SID> (NULL terminated) as the key */ |
488 | | |
489 | 0 | fstr_sprintf(keystr, "%s%s", PRIVPREFIX, dom_sid_str_buf(sid, &tmp)); |
490 | |
|
491 | 0 | return dbwrap_delete_bystring(db, keystr); |
492 | 0 | } |
493 | | |
494 | | /******************************************************************* |
495 | | *******************************************************************/ |
496 | | |
497 | | bool is_privileged_sid( const struct dom_sid *sid ) |
498 | 0 | { |
499 | 0 | uint64_t mask; |
500 | |
|
501 | 0 | return get_privileges( sid, &mask ); |
502 | 0 | } |
503 | | |
504 | | /******************************************************************* |
505 | | *******************************************************************/ |
506 | | |
507 | | bool grant_all_privileges( const struct dom_sid *sid ) |
508 | 0 | { |
509 | 0 | uint64_t mask; |
510 | |
|
511 | 0 | se_priv_put_all_privileges(&mask); |
512 | |
|
513 | 0 | return grant_privilege_bitmap( sid, mask ); |
514 | 0 | } |