2017-03-14 16:43:06 +13:00
# Unix SMB/CIFS implementation.
# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2017
#
# 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/>.
#
""" Tests for the Auth and AuthZ logging.
"""
import samba . tests
from samba . messaging import Messaging
from samba . dcerpc . messaging import MSG_AUTH_LOG , AUTH_EVENT_NAME
2019-02-26 10:53:43 +13:00
from samba . param import LoadParm
2017-03-14 16:43:06 +13:00
import time
import json
import os
2018-04-30 09:13:58 +12:00
import re
2019-02-28 16:55:31 +13:00
from samba import param
2017-03-14 16:43:06 +13:00
2018-12-17 10:04:42 +13:00
2017-03-14 16:43:06 +13:00
class AuthLogTestBase ( samba . tests . TestCase ) :
2019-02-28 16:55:31 +13:00
@classmethod
def setUpClass ( self ) :
2019-02-26 10:53:43 +13:00
# connect to the server's messaging bus (we need to explicitly load a
# different smb.conf here, because in all other respects this test
# wants to act as a separate remote client)
server_conf = os . getenv ( ' SERVERCONFFILE ' )
if server_conf :
lp_ctx = LoadParm ( filename_for_non_global_lp = server_conf )
else :
2019-02-28 16:55:31 +13:00
samba . tests . env_loadparm ( )
2018-04-30 10:35:25 +12:00
self . msg_ctx = Messaging ( ( 1 , ) , lp_ctx = lp_ctx )
2017-03-14 16:43:06 +13:00
self . msg_ctx . irpc_add_name ( AUTH_EVENT_NAME )
2019-02-26 11:10:46 +13:00
# Now switch back to using the client-side smb.conf. The tests will
# use the first interface in the client.conf (we need to strip off
# the subnet mask portion)
2019-02-28 16:55:31 +13:00
lp_ctx = samba . tests . env_loadparm ( )
2019-02-26 11:10:46 +13:00
client_ip_and_mask = lp_ctx . get ( ' interfaces ' ) [ 0 ]
client_ip = client_ip_and_mask . split ( ' / ' ) [ 0 ]
# the messaging ctx is the server's view of the world, so our own
# client IP will be the remoteAddress when connections are logged
self . remoteAddress = client_ip
2018-04-30 10:35:25 +12:00
def messageHandler ( context , msgType , src , message ) :
2017-03-14 16:43:06 +13:00
# This does not look like sub unit output and it
# makes these tests much easier to debug.
2018-03-09 13:38:42 +00:00
print ( message )
2017-03-14 16:43:06 +13:00
jsonMsg = json . loads ( message )
2018-04-30 10:35:25 +12:00
context [ " messages " ] . append ( jsonMsg )
2017-03-14 16:43:06 +13:00
2018-04-30 10:35:25 +12:00
self . context = { " messages " : [ ] }
2017-03-14 16:43:06 +13:00
self . msg_handler_and_context = ( messageHandler , self . context )
self . msg_ctx . register ( self . msg_handler_and_context ,
msg_type = MSG_AUTH_LOG )
self . remoteAddress = None
self . server = os . environ [ " SERVER " ]
self . connection = None
2019-02-28 16:55:31 +13:00
@classmethod
def tearDownClass ( self ) :
2017-03-14 16:43:06 +13:00
if self . msg_handler_and_context :
self . msg_ctx . deregister ( self . msg_handler_and_context ,
msg_type = MSG_AUTH_LOG )
2018-05-25 15:21:33 +12:00
self . msg_ctx . irpc_remove_name ( AUTH_EVENT_NAME )
2017-03-14 16:43:06 +13:00
2019-02-28 16:55:31 +13:00
def setUp ( self ) :
super ( AuthLogTestBase , self ) . setUp ( )
self . discardMessages ( )
2017-03-14 16:43:06 +13:00
def waitForMessages ( self , isLastExpectedMessage , connection = None ) :
2017-07-10 07:45:16 +12:00
""" Wait for all the expected messages to arrive
The connection is passed through to keep the connection alive
until all the logging messages have been received .
"""
2017-03-14 16:43:06 +13:00
2018-04-30 10:35:25 +12:00
def completed ( messages ) :
2017-03-14 16:43:06 +13:00
for message in messages :
2018-04-30 10:35:25 +12:00
if isRemote ( message ) and isLastExpectedMessage ( message ) :
2017-03-14 16:43:06 +13:00
return True
return False
2018-04-30 10:35:25 +12:00
def isRemote ( message ) :
2019-01-28 15:27:29 +13:00
if self . remoteAddress is None :
return True
2017-03-14 16:43:06 +13:00
remote = None
if message [ " type " ] == " Authorization " :
remote = message [ " Authorization " ] [ " remoteAddress " ]
elif message [ " type " ] == " Authentication " :
remote = message [ " Authentication " ] [ " remoteAddress " ]
else :
return False
try :
addr = remote . split ( " : " )
return addr [ 1 ] == self . remoteAddress
except IndexError :
return False
self . connection = connection
start_time = time . time ( )
2018-04-30 10:35:25 +12:00
while not completed ( self . context [ " messages " ] ) :
2017-03-14 16:43:06 +13:00
self . msg_ctx . loop_once ( 0.1 )
if time . time ( ) - start_time > 1 :
self . connection = None
return [ ]
self . connection = None
2018-11-28 14:15:23 +00:00
return list ( filter ( isRemote , self . context [ " messages " ] ) )
2017-03-21 09:59:45 +13:00
# Discard any previously queued messages.
2019-02-28 16:55:31 +13:00
@classmethod
2017-03-21 09:59:45 +13:00
def discardMessages ( self ) :
self . msg_ctx . loop_once ( 0.001 )
2018-04-30 10:35:25 +12:00
while len ( self . context [ " messages " ] ) :
2019-02-28 16:55:31 +13:00
self . context [ " messages " ] = [ ]
2017-03-21 09:59:45 +13:00
self . msg_ctx . loop_once ( 0.001 )
2017-07-10 07:45:16 +12:00
# Remove any NETLOGON authentication messages
# NETLOGON is only performed once per session, so to avoid ordering
# dependencies within the tests it's best to strip out NETLOGON messages.
#
def remove_netlogon_messages ( self , messages ) :
def is_not_netlogon ( msg ) :
if " Authentication " not in msg :
return True
sd = msg [ " Authentication " ] [ " serviceDescription " ]
return sd != " NETLOGON "
return list ( filter ( is_not_netlogon , messages ) )
2018-04-30 09:13:58 +12:00
GUID_RE = " [0-9a-f] {8} -[0-9a-f] {4} -[0-9a-f] {4} -[0-9a-f] {4} -[0-9a-f] {12} "
2018-04-30 10:35:25 +12:00
2018-04-30 09:13:58 +12:00
#
# Is the supplied GUID string correctly formatted
#
def is_guid ( self , guid ) :
return re . match ( self . GUID_RE , guid )