2017-03-14 06:43:06 +03: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/>.
#
2018-03-09 16:38:42 +03:00
from __future__ import print_function
2017-03-14 06:43:06 +03:00
""" 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
import time
import json
import os
2018-04-30 00:13:58 +03:00
import re
2018-04-30 01:35:25 +03:00
2018-11-16 15:46:16 +03:00
msg_ctxs = [ ]
2017-03-14 06:43:06 +03:00
2018-12-17 00:04:42 +03:00
2017-03-14 06:43:06 +03:00
class AuthLogTestBase ( samba . tests . TestCase ) :
def setUp ( self ) :
super ( AuthLogTestBase , self ) . setUp ( )
lp_ctx = self . get_loadparm ( )
2018-04-30 01:35:25 +03:00
self . msg_ctx = Messaging ( ( 1 , ) , lp_ctx = lp_ctx )
2018-11-16 15:46:16 +03:00
global msg_ctxs
msg_ctxs . append ( self . msg_ctx )
2017-03-14 06:43:06 +03:00
self . msg_ctx . irpc_add_name ( AUTH_EVENT_NAME )
2018-04-30 01:35:25 +03:00
def messageHandler ( context , msgType , src , message ) :
2017-03-14 06:43:06 +03:00
# This does not look like sub unit output and it
# makes these tests much easier to debug.
2018-03-09 16:38:42 +03:00
print ( message )
2017-03-14 06:43:06 +03:00
jsonMsg = json . loads ( message )
2018-04-30 01:35:25 +03:00
context [ " messages " ] . append ( jsonMsg )
2017-03-14 06:43:06 +03:00
2018-04-30 01:35:25 +03:00
self . context = { " messages " : [ ] }
2017-03-14 06:43:06 +03:00
self . msg_handler_and_context = ( messageHandler , self . context )
self . msg_ctx . register ( self . msg_handler_and_context ,
msg_type = MSG_AUTH_LOG )
2017-03-20 23:59:45 +03:00
self . discardMessages ( )
2017-03-14 06:43:06 +03:00
self . remoteAddress = None
self . server = os . environ [ " SERVER " ]
self . connection = None
def tearDown ( self ) :
if self . msg_handler_and_context :
self . msg_ctx . deregister ( self . msg_handler_and_context ,
msg_type = MSG_AUTH_LOG )
2018-05-25 06:21:33 +03:00
self . msg_ctx . irpc_remove_name ( AUTH_EVENT_NAME )
2017-03-14 06:43:06 +03:00
def waitForMessages ( self , isLastExpectedMessage , connection = None ) :
2017-07-09 22:45:16 +03: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 06:43:06 +03:00
2018-04-30 01:35:25 +03:00
def completed ( messages ) :
2017-03-14 06:43:06 +03:00
for message in messages :
2018-04-30 01:35:25 +03:00
if isRemote ( message ) and isLastExpectedMessage ( message ) :
2017-03-14 06:43:06 +03:00
return True
return False
2018-04-30 01:35:25 +03:00
def isRemote ( message ) :
2019-01-28 05:27:29 +03:00
if self . remoteAddress is None :
return True
2017-03-14 06:43:06 +03: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 01:35:25 +03:00
while not completed ( self . context [ " messages " ] ) :
2017-03-14 06:43:06 +03:00
self . msg_ctx . loop_once ( 0.1 )
if time . time ( ) - start_time > 1 :
self . connection = None
return [ ]
self . connection = None
2018-11-28 17:15:23 +03:00
return list ( filter ( isRemote , self . context [ " messages " ] ) )
2017-03-20 23:59:45 +03:00
# Discard any previously queued messages.
def discardMessages ( self ) :
self . msg_ctx . loop_once ( 0.001 )
2018-04-30 01:35:25 +03:00
while len ( self . context [ " messages " ] ) :
2017-03-20 23:59:45 +03:00
self . msg_ctx . loop_once ( 0.001 )
self . context [ " messages " ] = [ ]
2017-07-09 22:45:16 +03: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 00:13:58 +03: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 01:35:25 +03:00
2018-04-30 00:13:58 +03:00
#
# Is the supplied GUID string correctly formatted
#
def is_guid ( self , guid ) :
return re . match ( self . GUID_RE , guid )