2024-02-16 14:47:23 +13:00
# Unix SMB/CIFS implementation.
#
# Manage who can view service account passwords.
#
# Copyright (C) Catalyst.Net Ltd. 2024
#
# Written by Rob van der Linde <rob@catalyst.net.nz>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2024-03-12 16:47:58 +13:00
from samba . domain . models import Group , GroupManagedServiceAccount , Model , User
from samba . domain . models . exceptions import ModelError
2024-03-13 20:59:27 +13:00
from samba . getopt import CredentialsOptions , HostOptions , Option , SambaOptions
from samba . netcmd import Command , CommandError , SuperCommand
2024-02-16 14:47:23 +13:00
class cmd_service_account_group_msa_membership_show ( Command ) :
""" Display who is able to view the service account password. """
synopsis = " % prog -H <URL> [options] "
takes_optiongroups = {
" sambaopts " : SambaOptions ,
" credopts " : CredentialsOptions ,
" hostopts " : HostOptions ,
}
takes_options = [
Option ( " --name " ,
help = " Name of managed service account (required). " ,
dest = " name " , action = " store " , type = str , required = True ) ,
Option ( " --json " , help = " Output results in JSON format. " ,
dest = " output_format " , action = " store_const " , const = " json " ) ,
]
def run ( self , hostopts = None , sambaopts = None , credopts = None , name = None ,
output_format = None ) :
ldb = self . ldb_connect ( hostopts , sambaopts , credopts )
try :
gmsa = GroupManagedServiceAccount . find ( ldb , name )
except ModelError as e :
raise CommandError ( e )
if gmsa is None :
raise CommandError ( f " Group managed service account { name } not found. " )
try :
2024-03-12 13:06:31 +13:00
trustees = { sid : Model . get ( ldb , object_sid = sid , polymorphic = True ) for sid in gmsa . trustees }
2024-02-16 14:47:23 +13:00
except ModelError as e :
raise CommandError ( e )
if output_format == " json " :
self . print_json ( {
" dn " : gmsa . dn ,
2024-03-12 13:06:31 +13:00
" trustees " : [ trustee . dn if trustee else f " <SID= { sid } > " for sid , trustee in trustees . items ( ) ]
2024-02-16 14:47:23 +13:00
} )
else :
print ( f " Account-DN: { gmsa . dn } " , file = self . outf )
print ( " Accounts or groups that are able to retrieve this group managed service account password: " ,
file = self . outf )
2024-03-12 13:06:31 +13:00
for sid , trustee in trustees . items ( ) :
dn = trustee . dn if trustee else f " <SID= { sid } > "
print ( f " { dn } " , file = self . outf )
2024-02-16 14:47:23 +13:00
class cmd_service_account_group_msa_membership_add ( Command ) :
""" Add a password viewer. """
synopsis = " % prog -H <URL> [options] "
takes_optiongroups = {
" sambaopts " : SambaOptions ,
" credopts " : CredentialsOptions ,
" hostopts " : HostOptions ,
}
takes_options = [
Option ( " --name " ,
help = " Name of managed service account (required). " ,
dest = " name " , action = " store " , type = str , required = True ) ,
Option ( " --principal " ,
help = " Principal sAMAccountName or Dn to add (required). " ,
dest = " principal " , action = " store " , type = str , required = True ) ,
]
def run ( self , hostopts = None , sambaopts = None , credopts = None , name = None ,
principal = None ) :
ldb = self . ldb_connect ( hostopts , sambaopts , credopts )
try :
gmsa = GroupManagedServiceAccount . find ( ldb , name )
except ModelError as e :
raise CommandError ( e )
if gmsa is None :
raise CommandError ( f " Group managed service account { name } not found. " )
# Note that principal can be a user or group (by passing in a Dn).
# If the Dn is a group it will see it as a User but this doesn't matter.
try :
2024-03-12 12:40:12 +13:00
trustee = User . get_sid_for_principal ( ldb , principal )
2024-02-16 14:47:23 +13:00
except ModelError as e :
raise CommandError ( e )
if trustee is None :
2024-03-12 12:33:30 +13:00
raise CommandError ( f " Trustee { principal } not found. " )
2024-02-16 14:47:23 +13:00
try :
trustees = gmsa . trustees
except ModelError as e :
raise CommandError ( e )
2024-03-12 12:40:12 +13:00
if trustee in trustees :
print ( f " Trustee ' { principal } ' is already allowed to show managed passwords for: { gmsa } " ,
2024-02-16 14:47:23 +13:00
file = self . outf )
else :
2024-03-12 12:40:12 +13:00
gmsa . add_trustee ( trustee )
2024-02-16 14:47:23 +13:00
try :
gmsa . save ( ldb )
except ModelError as e :
raise CommandError ( e )
2024-03-12 12:40:12 +13:00
print ( f " Trustee ' { principal } ' is now allowed to show managed passwords for: { gmsa } " ,
2024-02-16 14:47:23 +13:00
file = self . outf )
class cmd_service_account_group_msa_membership_remove ( Command ) :
""" Remove a password viewer. """
synopsis = " % prog -H <URL> [options] "
takes_optiongroups = {
" sambaopts " : SambaOptions ,
" credopts " : CredentialsOptions ,
" hostopts " : HostOptions ,
}
takes_options = [
Option ( " --name " ,
help = " Name of managed service account (required). " ,
dest = " name " , action = " store " , type = str , required = True ) ,
Option ( " --principal " ,
help = " Principal sAMAccountName or Dn to remove (required). " ,
dest = " principal " , action = " store " , type = str , required = True ) ,
]
def run ( self , hostopts = None , sambaopts = None , credopts = None , name = None ,
principal = None ) :
ldb = self . ldb_connect ( hostopts , sambaopts , credopts )
try :
gmsa = GroupManagedServiceAccount . find ( ldb , name )
except ModelError as e :
raise CommandError ( e )
if gmsa is None :
raise CommandError ( f " Group managed service account { name } not found. " )
# Note that principal can be a user or group (by passing in a Dn).
# If the Dn is a group it will see it as a User but this doesn't matter.
try :
2024-03-12 12:40:12 +13:00
trustee = User . get_sid_for_principal ( ldb , principal )
2024-02-16 14:47:23 +13:00
except ModelError as e :
raise CommandError ( e )
if trustee is None :
2024-03-12 12:33:30 +13:00
raise CommandError ( f " Trustee { principal } not found. " )
2024-02-16 14:47:23 +13:00
try :
trustees = gmsa . trustees
except ModelError as e :
raise CommandError ( e )
2024-03-12 12:40:12 +13:00
if trustee not in trustees :
print ( f " Trustee ' { principal } ' cannot currently show managed passwords for: { gmsa } " ,
2024-02-16 14:47:23 +13:00
file = self . outf )
else :
2024-03-12 12:40:12 +13:00
gmsa . remove_trustee ( trustee )
2024-02-16 14:47:23 +13:00
try :
gmsa . save ( ldb )
except ModelError as e :
raise CommandError ( e )
2024-03-12 12:40:12 +13:00
print ( f " Trustee ' { principal } ' removed access to show managed passwords for: { gmsa } " ,
2024-02-16 14:47:23 +13:00
file = self . outf )
class cmd_service_account_group_msa_membership ( SuperCommand ) :
""" View and manage password retrieval for service account. """
# set sddl
subcommands = {
" show " : cmd_service_account_group_msa_membership_show ( ) ,
" add " : cmd_service_account_group_msa_membership_add ( ) ,
" remove " : cmd_service_account_group_msa_membership_remove ( ) ,
}