2021-04-29 11:58:11 +03:00
#!/usr/bin/env python3
# Unix SMB/CIFS implementation.
# Copyright (C) Stefan Metzmacher 2020
# Copyright (C) 2021 Catalyst.Net Ltd
#
# 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/>.
#
import sys
import os
2022-11-29 16:14:32 +03:00
sys . path . insert ( 0 , " bin/python " )
os . environ [ " PYTHONUNBUFFERED " ] = " 1 "
2021-10-29 05:07:07 +03:00
import ldb
2021-08-24 18:11:24 +03:00
from ldb import LdbError , ERR_OPERATIONS_ERROR , SCOPE_BASE , SCOPE_SUBTREE
2021-04-29 11:58:11 +03:00
from samba . dcerpc import security
from samba . ndr import ndr_unpack
from samba . samdb import SamDB
2021-08-24 18:11:24 +03:00
from samba import credentials
2021-04-29 11:58:11 +03:00
from samba . tests . krb5 . kdc_base_test import KDCBaseTest
global_asn1_print = False
global_hexdump = False
class LdapTests ( KDCBaseTest ) :
""" Test for LDAP authentication using Kerberos credentials stored in a
credentials cache file .
"""
def test_ldap ( self ) :
2021-10-29 05:07:07 +03:00
self . _run_ldap_test ( )
def test_ldap_rename ( self ) :
self . _run_ldap_test ( rename = True )
2021-08-24 18:11:24 +03:00
def test_ldap_no_pac ( self ) :
2021-10-29 05:07:07 +03:00
self . _run_ldap_test ( include_pac = False ,
2021-08-24 18:11:24 +03:00
expect_anon = True , allow_error = True )
2021-10-29 05:07:07 +03:00
def _run_ldap_test ( self , rename = False , include_pac = True ,
2021-08-24 18:11:24 +03:00
expect_anon = False , allow_error = False ) :
2021-04-29 11:58:11 +03:00
# Create a user account and a machine account, along with a Kerberos
# credentials cache file where the service ticket authenticating the
# user are stored.
2021-06-16 02:04:00 +03:00
samdb = self . get_samdb ( )
mach_name = samdb . host_dns_name ( )
2021-04-29 11:58:11 +03:00
service = " ldap "
# Create the user account.
2021-10-29 05:07:07 +03:00
user_credentials = self . get_cached_creds (
account_type = self . AccountType . USER ,
use_cache = False )
user_name = user_credentials . get_username ( )
2021-04-29 11:58:11 +03:00
2021-10-22 01:37:31 +03:00
mach_credentials = self . get_dc_creds ( )
2021-04-29 11:58:11 +03:00
# Talk to the KDC to obtain the service ticket, which gets placed into
# the cache. The machine account name has to match the name in the
# ticket, to ensure that the krbtgt ticket doesn't also need to be
# stored.
( creds , cachefile ) = self . create_ccache_with_user ( user_credentials ,
2021-10-22 01:37:31 +03:00
mach_credentials ,
service ,
2021-08-24 18:11:24 +03:00
mach_name ,
pac = include_pac )
# Remove the cached credentials file.
self . addCleanup ( os . remove , cachefile . name )
2021-04-29 11:58:11 +03:00
# Retrieve the user account's SID.
2021-06-16 02:04:00 +03:00
ldb_res = samdb . search ( scope = SCOPE_SUBTREE ,
expression = " (sAMAccountName= %s ) " % user_name ,
attrs = [ " objectSid " ] )
2021-04-29 11:58:11 +03:00
self . assertEqual ( 1 , len ( ldb_res ) )
sid = ndr_unpack ( security . dom_sid , ldb_res [ 0 ] [ " objectSid " ] [ 0 ] )
2021-10-29 05:07:07 +03:00
if rename :
# Rename the account.
new_name = self . get_new_username ( )
msg = ldb . Message ( user_credentials . get_dn ( ) )
msg [ ' sAMAccountName ' ] = ldb . MessageElement ( new_name ,
ldb . FLAG_MOD_REPLACE ,
' sAMAccountName ' )
samdb . modify ( msg )
# Authenticate in-process to the machine account using the user's
# cached credentials.
2021-08-24 18:11:24 +03:00
# Connect to the machine account and retrieve the user SID.
try :
ldb_as_user = SamDB ( url = " ldap:// %s " % mach_name ,
credentials = creds ,
lp = self . get_lp ( ) )
except LdbError as e :
if not allow_error :
self . fail ( )
enum , estr = e . args
self . assertEqual ( ERR_OPERATIONS_ERROR , enum )
2021-10-29 00:27:41 +03:00
self . assertIn ( ' NT_STATUS_NO_IMPERSONATION_TOKEN ' , estr )
2021-08-24 18:11:24 +03:00
return
ldb_res = ldb_as_user . search ( ' ' ,
scope = SCOPE_BASE ,
attrs = [ " tokenGroups " ] )
self . assertEqual ( 1 , len ( ldb_res ) )
token_groups = ldb_res [ 0 ] [ " tokenGroups " ]
token_sid = ndr_unpack ( security . dom_sid , token_groups [ 0 ] )
if expect_anon :
# Ensure we got an anonymous token.
self . assertEqual ( security . SID_NT_ANONYMOUS , str ( token_sid ) )
token_sid = ndr_unpack ( security . dom_sid , token_groups [ 1 ] )
self . assertEqual ( security . SID_NT_NETWORK , str ( token_sid ) )
if len ( token_groups ) > = 3 :
token_sid = ndr_unpack ( security . dom_sid , token_groups [ 2 ] )
self . assertEqual ( security . SID_NT_THIS_ORGANISATION ,
str ( token_sid ) )
else :
# Ensure that they match.
self . assertEqual ( sid , token_sid )
def test_ldap_anonymous ( self ) :
samdb = self . get_samdb ( )
mach_name = samdb . host_dns_name ( )
anon_creds = credentials . Credentials ( )
anon_creds . set_anonymous ( )
2021-04-29 11:58:11 +03:00
# Connect to the machine account and retrieve the user SID.
ldb_as_user = SamDB ( url = " ldap:// %s " % mach_name ,
2021-08-24 18:11:24 +03:00
credentials = anon_creds ,
2021-06-16 02:40:41 +03:00
lp = self . get_lp ( ) )
2021-04-29 11:58:11 +03:00
ldb_res = ldb_as_user . search ( ' ' ,
scope = SCOPE_BASE ,
attrs = [ " tokenGroups " ] )
self . assertEqual ( 1 , len ( ldb_res ) )
2021-08-24 18:11:24 +03:00
# Ensure we got an anonymous token.
2021-04-29 11:58:11 +03:00
token_sid = ndr_unpack ( security . dom_sid , ldb_res [ 0 ] [ " tokenGroups " ] [ 0 ] )
2021-08-24 18:11:24 +03:00
self . assertEqual ( security . SID_NT_ANONYMOUS , str ( token_sid ) )
self . assertEqual ( len ( ldb_res [ 0 ] [ " tokenGroups " ] ) , 1 )
2021-04-29 11:58:11 +03:00
if __name__ == " __main__ " :
2021-10-08 01:48:41 +03:00
global_asn1_print = False
global_hexdump = False
2021-04-29 11:58:11 +03:00
import unittest
unittest . main ( )