Improved check of tunneled requests

This commit is contained in:
Adolfo Gómez García 2021-09-09 12:56:25 +02:00
parent 9a3913cc42
commit ede23ad793
6 changed files with 10 additions and 129 deletions

View File

@ -128,7 +128,6 @@ class TunnelTicket(Handler):
log.doLog(user.manager, log.INFO, msg)
log.doLog(userService, log.INFO, msg)
# Generate new, notify only, ticket
rstr = managers.cryptoManager().randomString(length=8)
notifyTicket = models.TicketStore.create_for_tunnel(
userService=userService,
port=port,

View File

@ -54,13 +54,13 @@ def dict2resp(dct: typing.Mapping[typing.Any, typing.Any]) -> str:
return '\r'.join((str(k) + '\t' + str(v) for k, v in dct.items()))
@auth.trustedSourceRequired
def guacamole(
request: ExtendedHttpRequestWithUser, token: str, tunnelId: str
) -> HttpResponse:
if not TunnelToken.validateToken(token):
logger.error('Invalid token %s from %s', token, request.ip)
return HttpResponse(ERROR, content_type=CONTENT_TYPE)
# TODO: Check the authId validity
logger.debug('Received credentials request for tunnel id %s', tunnelId)
try:
@ -73,11 +73,13 @@ def guacamole(
# Extra check that the ticket data belongs to original requested user service/user
if 'ticket-info' in val:
ti = typing.cast(typing.Mapping[str, str], val['ticket-info'])
ti = typing.cast(typing.Mapping[str, str], val['ticket-info']) # recast to dict
del val['ticket-info'] # Do not send this data to guacamole!! :)
try:
userService = UserService.objects.get(uuid=ti['userService'])
if not userService.isUsable():
raise Exception() # Not usable, so we will not use it :)
# Log message and event
protocol = 'RDS' if 'remote-app' in val else val['protocol'].upper()
host = val.get('hostname', '0.0.0.0')
@ -99,12 +101,12 @@ def guacamole(
logger.error(
'The requested guacamole userservice does not exists anymore'
)
raise
raise # Let it be handled by the upper layers
if userService.user.uuid != ti['user']:
logger.error(
'The requested userservice has changed owner and is not accesible'
)
raise Exception()
raise Exception() # Let it be handled by the upper layers
if 'password' in val:
val['password'] = cryptoManager().symDecrpyt(val['password'], scrambler)

View File

@ -1,41 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
@author: Adolfo Gómez, dkmaster at dkmon dot com
"""
from django.conf.urls import url
from .views import pam
urlpatterns = [
# Old, compat
url(r'^pam$', pam, name='dispatcher.pam'),
# New
url(r'^uds/pam$', pam, name='dispatcher.pam'),
]

View File

@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
@author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import logging
from django.http import HttpResponseNotAllowed, HttpResponse, HttpRequest
from uds.models import TicketStore
from uds.core.auths import auth
from uds.core.util.request import ExtendedHttpRequestWithUser
logger = logging.getLogger(__name__)
# We will use the cache to "hold" the tickets valid for users
@auth.trustedSourceRequired
def pam(request: ExtendedHttpRequestWithUser) -> HttpResponse:
response = ''
if request.method == 'POST':
return HttpResponseNotAllowed(['GET'])
if 'id' in request.GET and 'pass' in request.GET:
# This is an "auth" request
ids = request.GET.getlist('id')
response = '0'
# If request is not forged...
if len(ids) == 1:
userId = ids[0]
logger.debug(
"Auth request for user [%s] and pass [%s]",
request.GET['id'],
request.GET['pass'],
)
try:
password = TicketStore.get(userId)
if password == request.GET['pass']:
response = '1'
except Exception:
# Non existing ticket, log it and stop
logger.info('Invalid access from %s using user %s', request.ip, userId)
else:
logger.warning(
'Invalid request from %s: %s',
request.ip,
[v for v in request.GET.lists()],
)
elif 'uid' in request.GET:
# This is an "get name for id" call
logger.debug("NSS Request for id [%s]", request.GET['uid'])
response = '10000 udstmp'
elif 'name' in request.GET:
logger.debug("NSS Request for username [%s]", request.GET['name'])
response = '10000 udstmp'
return HttpResponse(response, content_type='text/plain')

View File

@ -236,6 +236,10 @@ class TicketStore(UUIDModel):
# if not found any, will raise an execption
user = User.objects.get(uuid=data['u'])
userService = UserService.objects.get(uuid=data['s'], user=user)
# Ensure userservice is usable
if not userService.isUsable():
raise Exception('Service is not usable') # Not usable, so we will not use it :)
host = data['h']
if not host: