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
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #define PRIVPREFIX "PRIV_"
28 #define GENERATE_LUID_LOW(x) (x)+1;
30 static const SE_PRIV se_priv_all = SE_ALL_PRIVS;
31 static const SE_PRIV se_priv_end = SE_END;
33 /* Define variables for all privileges so we can use the
34 SE_PRIV* in the various se_priv_XXX() functions */
36 const SE_PRIV se_priv_none = SE_NONE;
37 const SE_PRIV se_machine_account = SE_MACHINE_ACCOUNT;
38 const SE_PRIV se_print_operator = SE_PRINT_OPERATOR;
39 const SE_PRIV se_add_users = SE_ADD_USERS;
40 const SE_PRIV se_disk_operators = SE_DISK_OPERATOR;
41 const SE_PRIV se_remote_shutdown = SE_REMOTE_SHUTDOWN;
42 const SE_PRIV se_restore = SE_RESTORE;
44 /********************************************************************
45 This is a list of privileges reported by a WIndows 2000 SP4 AD DC
46 just for reference purposes:
48 SeCreateTokenPrivilege Create a token object
49 SeAssignPrimaryTokenPrivilege Replace a process level token
50 SeLockMemoryPrivilege Lock pages in memory
51 SeIncreaseQuotaPrivilege Increase quotas
52 SeMachineAccountPrivilege Add workstations to domain
53 SeTcbPrivilege Act as part of the operating system
54 SeSecurityPrivilege Manage auditing and security log
55 SeTakeOwnershipPrivilege Take ownership of files or other objects
56 SeLoadDriverPrivilege Load and unload device drivers
57 SeSystemProfilePrivilege Profile system performance
58 SeSystemtimePrivilege Change the system time
59 SeProfileSingleProcessPrivilege Profile single process
60 SeIncreaseBasePriorityPrivilege Increase scheduling priority
61 SeCreatePagefilePrivilege Create a pagefile
62 SeCreatePermanentPrivilege Create permanent shared objects
63 SeBackupPrivilege Back up files and directories
64 SeRestorePrivilege Restore files and directories
65 SeShutdownPrivilege Shut down the system
66 SeDebugPrivilege Debug programs
67 SeAuditPrivilege Generate security audits
68 SeSystemEnvironmentPrivilege Modify firmware environment values
69 SeChangeNotifyPrivilege Bypass traverse checking
70 SeRemoteShutdownPrivilege Force shutdown from a remote system
71 SeUndockPrivilege Remove computer from docking station
72 SeSyncAgentPrivilege Synchronize directory service data
73 SeEnableDelegationPrivilege Enable computer and user accounts to be trusted for delegation
74 SeManageVolumePrivilege Perform volume maintenance tasks
75 SeImpersonatePrivilege Impersonate a client after authentication
76 SeCreateGlobalPrivilege Create global objects
78 ********************************************************************/
82 #if 0 /* usrmgr will display these twice if you include them. We don't
83 use them but we'll keep the bitmasks reserved in privileges.h anyways */
85 {SE_NETWORK_LOGON, "SeNetworkLogonRight", "Access this computer from network"},
86 {SE_INTERACTIVE_LOGON, "SeInteractiveLogonRight", "Log on locally"},
87 {SE_BATCH_LOGON, "SeBatchLogonRight", "Log on as a batch job"},
88 {SE_SERVICE_LOGON, "SeServiceLogonRight", "Log on as a service"},
90 {SE_MACHINE_ACCOUNT, "SeMachineAccountPrivilege", "Add machines to domain"},
91 {SE_PRINT_OPERATOR, "SePrintOperatorPrivilege", "Manage printers"},
92 {SE_ADD_USERS, "SeAddUsersPrivilege", "Add users and groups to the domain"},
93 {SE_REMOTE_SHUTDOWN, "SeRemoteShutdownPrivilege", "Force shutdown from a remote system"},
94 {SE_DISK_OPERATOR, "SeDiskOperatorPrivilege", "Manage disk shares"},
95 {SE_BACKUP, "SeBackupPrivilege", "Back up files and directories"},
96 {SE_RESTORE, "SeRestorePrivilege", "Restore files and directories"},
97 {SE_TAKE_OWNERSHIP, "SeTakeOwnershipPrivilege", "Take ownership of files or other objects"},
113 /***************************************************************************
114 copy an SE_PRIV structure
115 ****************************************************************************/
117 BOOL se_priv_copy( SE_PRIV *dst, const SE_PRIV *src )
122 memcpy( dst, src, sizeof(SE_PRIV) );
127 /***************************************************************************
128 combine 2 SE_PRIV structures and store the resulting set in mew_mask
129 ****************************************************************************/
131 void se_priv_add( SE_PRIV *mask, const SE_PRIV *addpriv )
135 for ( i=0; i<SE_PRIV_MASKSIZE; i++ ) {
136 mask->mask[i] |= addpriv->mask[i];
140 /***************************************************************************
141 remove one SE_PRIV sytucture from another and store the resulting set
143 ****************************************************************************/
145 void se_priv_remove( SE_PRIV *mask, const SE_PRIV *removepriv )
149 for ( i=0; i<SE_PRIV_MASKSIZE; i++ ) {
150 mask->mask[i] &= ~removepriv->mask[i];
154 /***************************************************************************
155 invert a given SE_PRIV and store the set in new_mask
156 ****************************************************************************/
158 static void se_priv_invert( SE_PRIV *new_mask, const SE_PRIV *mask )
162 se_priv_copy( &allprivs, &se_priv_all );
163 se_priv_remove( &allprivs, mask );
164 se_priv_copy( new_mask, &allprivs );
167 /***************************************************************************
168 check if 2 SE_PRIV structure are equal
169 ****************************************************************************/
171 static BOOL se_priv_equal( const SE_PRIV *mask1, const SE_PRIV *mask2 )
173 return ( memcmp(mask1, mask2, sizeof(SE_PRIV)) == 0 );
176 /***************************************************************************
177 check if a SE_PRIV has any assigned privileges
178 ****************************************************************************/
180 static BOOL se_priv_empty( const SE_PRIV *mask )
185 se_priv_copy( &p1, mask );
187 for ( i=0; i<SE_PRIV_MASKSIZE; i++ ) {
188 p1.mask[i] &= se_priv_all.mask[i];
191 return se_priv_equal( &p1, &se_priv_none );
194 /*********************************************************************
195 Lookup the SE_PRIV value for a privilege name
196 *********************************************************************/
198 BOOL se_priv_from_name( const char *name, SE_PRIV *mask )
202 for ( i=0; !se_priv_equal(&privs[i].se_priv, &se_priv_end); i++ ) {
203 if ( strequal( privs[i].name, name ) ) {
204 se_priv_copy( mask, &privs[i].se_priv );
212 /***************************************************************************
213 dump an SE_PRIV structure to the log files
214 ****************************************************************************/
216 void dump_se_priv( int dbg_cl, int dbg_lvl, const SE_PRIV *mask )
220 DEBUGADDC( dbg_cl, dbg_lvl,("SE_PRIV "));
222 for ( i=0; i<SE_PRIV_MASKSIZE; i++ ) {
223 DEBUGADDC( dbg_cl, dbg_lvl,(" 0x%x", mask->mask[i] ));
226 DEBUGADDC( dbg_cl, dbg_lvl, ("\n"));
229 /***************************************************************************
230 Retrieve the privilege mask (set) for a given SID
231 ****************************************************************************/
233 static BOOL get_privileges( const DOM_SID *sid, SE_PRIV *mask )
235 TDB_CONTEXT *tdb = get_account_pol_tdb();
239 /* Fail if the admin has not enable privileges */
241 if ( !lp_enable_privileges() ) {
248 /* PRIV_<SID> (NULL terminated) as the key */
250 fstr_sprintf( keystr, "%s%s", PRIVPREFIX, sid_string_static(sid) );
252 key.dsize = strlen(keystr) + 1;
254 data = tdb_fetch( tdb, key );
257 DEBUG(3,("get_privileges: No privileges assigned to SID [%s]\n",
258 sid_string_static(sid)));
262 SMB_ASSERT( data.dsize == sizeof( SE_PRIV ) );
264 se_priv_copy( mask, (SE_PRIV*)data.dptr );
265 SAFE_FREE(data.dptr);
270 /***************************************************************************
271 Store the privilege mask (set) for a given SID
272 ****************************************************************************/
274 static BOOL set_privileges( const DOM_SID *sid, SE_PRIV *mask )
276 TDB_CONTEXT *tdb = get_account_pol_tdb();
280 if ( !lp_enable_privileges() )
286 /* PRIV_<SID> (NULL terminated) as the key */
288 fstr_sprintf( keystr, "%s%s", PRIVPREFIX, sid_string_static(sid) );
290 key.dsize = strlen(keystr) + 1;
292 /* no packing. static size structure, just write it out */
294 data.dptr = (char*)mask;
295 data.dsize = sizeof(SE_PRIV);
297 return ( tdb_store(tdb, key, data, TDB_REPLACE) != -1 );
300 /****************************************************************************
301 check if the privilege is in the privilege list
302 ****************************************************************************/
304 static BOOL is_privilege_assigned( SE_PRIV *privileges, const SE_PRIV *check )
308 if ( !privileges || !check )
311 /* everyone has privileges if you aren't checking for any */
313 if ( se_priv_empty( check ) ) {
314 DEBUG(1,("is_privilege_assigned: no privileges in check_mask!\n"));
318 se_priv_copy( &p1, check );
320 /* invert the SE_PRIV we want to check for and remove that from the
321 original set. If we are left with the SE_PRIV we are checking
322 for then return True */
324 se_priv_invert( &p1, check );
325 se_priv_copy( &p2, privileges );
326 se_priv_remove( &p2, &p1 );
328 return se_priv_equal( &p2, check );
331 /****************************************************************************
332 check if the privilege is in the privilege list
333 ****************************************************************************/
335 static BOOL is_any_privilege_assigned( SE_PRIV *privileges, const SE_PRIV *check )
339 if ( !privileges || !check )
342 /* everyone has privileges if you aren't checking for any */
344 if ( se_priv_empty( check ) ) {
345 DEBUG(1,("is_any_privilege_assigned: no privileges in check_mask!\n"));
349 se_priv_copy( &p1, check );
351 /* invert the SE_PRIV we want to check for and remove that from the
352 original set. If we are left with the SE_PRIV we are checking
353 for then return True */
355 se_priv_invert( &p1, check );
356 se_priv_copy( &p2, privileges );
357 se_priv_remove( &p2, &p1 );
359 /* see if we have any bits left */
361 return !se_priv_empty( &p2 );
364 /****************************************************************************
365 add a privilege to a privilege array
366 ****************************************************************************/
368 static BOOL privilege_set_add(PRIVILEGE_SET *priv_set, LUID_ATTR set)
372 /* we can allocate memory to add the new privilege */
374 new_set = TALLOC_REALLOC_ARRAY(priv_set->mem_ctx, priv_set->set, LUID_ATTR, priv_set->count + 1);
376 DEBUG(0,("privilege_set_add: failed to allocate memory!\n"));
380 new_set[priv_set->count].luid.high = set.luid.high;
381 new_set[priv_set->count].luid.low = set.luid.low;
382 new_set[priv_set->count].attr = set.attr;
385 priv_set->set = new_set;
390 /*********************************************************************
391 Generate the LUID_ATTR structure based on a bitmask
392 *********************************************************************/
394 LUID_ATTR get_privilege_luid( SE_PRIV *mask )
400 priv_luid.luid.high = 0;
402 for ( i=0; !se_priv_equal(&privs[i].se_priv, &se_priv_end); i++ ) {
404 if ( se_priv_equal( &privs[i].se_priv, mask ) ) {
405 priv_luid.luid.low = GENERATE_LUID_LOW(i);
413 /*********************************************************************
414 Generate the LUID_ATTR structure based on a bitmask
415 *********************************************************************/
417 const char* get_privilege_dispname( const char *name )
421 for ( i=0; !se_priv_equal(&privs[i].se_priv, &se_priv_end); i++ ) {
423 if ( strequal( privs[i].name, name ) ) {
424 return privs[i].description;
431 /*********************************************************************
432 get a list of all privleges for all sids the in list
433 *********************************************************************/
435 BOOL get_privileges_for_sids(SE_PRIV *privileges, DOM_SID *slist, int scount)
441 se_priv_copy( privileges, &se_priv_none );
443 for ( i=0; i<scount; i++ ) {
444 /* don't add unless we actually have a privilege assigned */
446 if ( !get_privileges( &slist[i], &mask ) )
449 DEBUG(5,("get_privileges_for_sids: sid = %s\nPrivilege set:\n",
450 sid_string_static(&slist[i])));
451 dump_se_priv( DBGC_ALL, 5, &mask );
453 se_priv_add( privileges, &mask );
461 /*********************************************************************
462 travseral functions for privilege_enumerate_accounts
463 *********************************************************************/
465 static int priv_traverse_fn(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
467 PRIV_SID_LIST *priv = state;
468 int prefixlen = strlen(PRIVPREFIX);
472 /* easy check first */
474 if ( data.dsize != sizeof(SE_PRIV) )
477 /* check we have a PRIV_+SID entry */
479 if ( strncmp(key.dptr, PRIVPREFIX, prefixlen) != 0)
482 /* check to see if we are looking for a particular privilege */
484 if ( !se_priv_equal(&priv->privilege, &se_priv_none) ) {
487 se_priv_copy( &mask, (SE_PRIV*)data.dptr );
489 /* if the SID does not have the specified privilege
492 if ( !is_privilege_assigned( &mask, &priv->privilege) )
496 fstrcpy( sid_string, &key.dptr[strlen(PRIVPREFIX)] );
498 if ( !string_to_sid(&sid, sid_string) ) {
499 DEBUG(0,("travsersal_fn_enum__acct: Could not convert SID [%s]\n",
504 add_sid_to_array( NULL, &sid, &priv->sids.list, &priv->sids.count );
509 /*********************************************************************
510 Retreive list of privileged SIDs (for _lsa_enumerate_accounts()
511 *********************************************************************/
513 NTSTATUS privilege_enumerate_accounts(DOM_SID **sids, int *num_sids)
515 TDB_CONTEXT *tdb = get_account_pol_tdb();
520 se_priv_copy( &priv.privilege, &se_priv_none );
522 tdb_traverse( tdb, priv_traverse_fn, &priv);
524 /* give the memory away; caller will free */
526 *sids = priv.sids.list;
527 *num_sids = priv.sids.count;
532 /***************************************************************************
534 ****************************************************************************/
536 BOOL grant_privilege(const DOM_SID *sid, const SE_PRIV *priv_mask)
538 SE_PRIV old_mask, new_mask;
540 if ( get_privileges( sid, &old_mask ) )
541 se_priv_copy( &new_mask, &old_mask );
543 se_priv_copy( &new_mask, &se_priv_none );
545 se_priv_add( &new_mask, priv_mask );
547 DEBUG(10,("grant_privilege: %s\n", sid_string_static(sid)));
549 DEBUGADD( 10, ("original privilege mask:\n"));
550 dump_se_priv( DBGC_ALL, 10, &old_mask );
552 DEBUGADD( 10, ("new privilege mask:\n"));
553 dump_se_priv( DBGC_ALL, 10, &new_mask );
555 return set_privileges( sid, &new_mask );
558 /*********************************************************************
559 Add a privilege based on its name
560 *********************************************************************/
562 BOOL grant_privilege_by_name(DOM_SID *sid, const char *name)
566 for ( i=0; !se_priv_equal(&privs[i].se_priv, &se_priv_end); i++ ) {
567 if ( strequal(privs[i].name, name) ) {
568 return grant_privilege( sid, &privs[i].se_priv );
572 DEBUG(3, ("grant_privilege_by_name: No Such Privilege Found (%s)\n", name));
577 /***************************************************************************
578 Remove privilege from sid
579 ****************************************************************************/
581 BOOL revoke_privilege(const DOM_SID *sid, const SE_PRIV *priv_mask)
585 /* if the user has no privileges, then we can't revoke any */
587 if ( !get_privileges( sid, &mask ) )
590 DEBUG(10,("revoke_privilege: %s\n", sid_string_static(sid)));
592 DEBUGADD( 10, ("original privilege mask:\n"));
593 dump_se_priv( DBGC_ALL, 10, &mask );
595 se_priv_remove( &mask, priv_mask );
597 DEBUGADD( 10, ("new privilege mask:\n"));
598 dump_se_priv( DBGC_ALL, 10, &mask );
600 return set_privileges( sid, &mask );
603 /*********************************************************************
604 Revoke all privileges
605 *********************************************************************/
607 BOOL revoke_all_privileges( DOM_SID *sid )
609 return revoke_privilege( sid, &se_priv_all );
612 /*********************************************************************
613 Add a privilege based on its name
614 *********************************************************************/
616 BOOL revoke_privilege_by_name(DOM_SID *sid, const char *name)
620 for ( i=0; !se_priv_equal(&privs[i].se_priv, &se_priv_end); i++ ) {
621 if ( strequal(privs[i].name, name) ) {
622 return revoke_privilege( sid, &privs[i].se_priv );
626 DEBUG(3, ("revoke_privilege_by_name: No Such Privilege Found (%s)\n", name));
631 /***************************************************************************
632 Retrieve the SIDs assigned to a given privilege
633 ****************************************************************************/
635 NTSTATUS privilege_create_account(const DOM_SID *sid )
637 return ( grant_privilege(sid, &se_priv_none) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL);
640 /****************************************************************************
641 initialise a privilege list and set the talloc context
642 ****************************************************************************/
644 NTSTATUS privilege_set_init(PRIVILEGE_SET *priv_set)
648 ZERO_STRUCTP( priv_set );
650 mem_ctx = talloc_init("privilege set");
652 DEBUG(0,("privilege_set_init: failed to initialize talloc ctx!\n"));
653 return NT_STATUS_NO_MEMORY;
656 priv_set->mem_ctx = mem_ctx;
661 /****************************************************************************
662 initialise a privilege list and with someone else's talloc context
663 ****************************************************************************/
665 NTSTATUS privilege_set_init_by_ctx(TALLOC_CTX *mem_ctx, PRIVILEGE_SET *priv_set)
667 ZERO_STRUCTP( priv_set );
669 priv_set->mem_ctx = mem_ctx;
670 priv_set->ext_ctx = True;
675 /****************************************************************************
676 Free all memory used by a PRIVILEGE_SET
677 ****************************************************************************/
679 void privilege_set_free(PRIVILEGE_SET *priv_set)
684 if ( !( priv_set->ext_ctx ) )
685 talloc_destroy( priv_set->mem_ctx );
687 ZERO_STRUCTP( priv_set );
690 /****************************************************************************
691 duplicate alloc luid_attr
692 ****************************************************************************/
694 NTSTATUS dup_luid_attr(TALLOC_CTX *mem_ctx, LUID_ATTR **new_la, LUID_ATTR *old_la, int count)
701 *new_la = TALLOC_ARRAY(mem_ctx, LUID_ATTR, count);
703 DEBUG(0,("dup_luid_attr: failed to alloc new LUID_ATTR array [%d]\n", count));
704 return NT_STATUS_NO_MEMORY;
707 for (i=0; i<count; i++) {
708 (*new_la)[i].luid.high = old_la[i].luid.high;
709 (*new_la)[i].luid.low = old_la[i].luid.low;
710 (*new_la)[i].attr = old_la[i].attr;
716 /****************************************************************************
717 Does the user have the specified privilege ? We only deal with one privilege
719 *****************************************************************************/
721 BOOL user_has_privileges(NT_USER_TOKEN *token, const SE_PRIV *privilege)
726 return is_privilege_assigned( &token->privileges, privilege );
729 /****************************************************************************
730 Does the user have any of the specified privileges ? We only deal with one privilege
732 *****************************************************************************/
734 BOOL user_has_any_privilege(NT_USER_TOKEN *token, const SE_PRIV *privilege)
739 return is_any_privilege_assigned( &token->privileges, privilege );
742 /****************************************************************************
743 Convert a LUID to a named string
744 ****************************************************************************/
746 char* luid_to_privilege_name(const LUID *set)
749 int max = count_all_privileges();
754 if ( set->low > max )
757 fstrcpy( name, privs[set->low - 1].name );
762 /*******************************************************************
763 return the number of elements in the privlege array
764 *******************************************************************/
766 int count_all_privileges( void )
773 /* loop over the array and count it */
774 for ( count=0; !se_priv_equal(&privs[count].se_priv, &se_priv_end); count++ ) ;
779 /*******************************************************************
780 *******************************************************************/
782 BOOL se_priv_to_privilege_set( PRIVILEGE_SET *set, SE_PRIV *mask )
785 uint32 num_privs = count_all_privileges();
791 for ( i=0; i<num_privs; i++ ) {
792 if ( !is_privilege_assigned(mask, &privs[i].se_priv) )
795 luid.luid.low = GENERATE_LUID_LOW(i);
797 if ( !privilege_set_add( set, luid ) )
804 /*******************************************************************
805 *******************************************************************/
807 BOOL privilege_set_to_se_priv( SE_PRIV *mask, PRIVILEGE_SET *privset )
810 uint32 num_privs = count_all_privileges();
812 ZERO_STRUCTP( mask );
814 for ( i=0; i<privset->count; i++ ) {
817 /* sanity check for invalid privilege. we really
818 only care about the low 32 bits */
820 if ( privset->set[i].luid.high != 0 )
823 /* make sure :LUID.low is in range */
824 if ( privset->set[i].luid.low == 0 || privset->set[i].luid.low > num_privs )
827 r = privs[privset->set[i].luid.low - 1].se_priv;
828 se_priv_add( mask, &r );
834 /*******************************************************************
835 *******************************************************************/
837 BOOL is_privileged_sid( DOM_SID *sid )
841 return get_privileges( sid, &mask );