#!/usr/bin/perl -Tw -I. -I/usr/local/positive/Perl use strict; use Pos::Web::Client; use Pos::web; use Digest::MD5; use Pos::Webtop::Auth; use Pos::Webtop::Toolbox; package Pos::Webtop::Web::Client; our @ISA = qw( Pos::Web::Client ); sub new { my $this = shift; my $class = ref($this) || $this; my ($self,$db) = @{{@_}}{qw/self db/}; if ( not defined $self ) { $self = {}; bless($self,$class); } if ( not defined $db ) { warn ref($self) . "::setup(): db was undef\n"; return undef; } $self->{'webtopDb'} = $db; $self->{'expired'} = 0; $self->{'trialExpired'} = 0; $self->{'challenge'} = ''; # setup base class Pos::Web::Client->new( self=> $self ); $self->setupSession(); return $self; } sub setupSession { my $self = shift; my $db = $self->{'webtopDb'}; $self->{'persist'} = 0; $self->{'userId'} = undef; $self->{'sessionId'} = undef; $self->{'sessionHash'} = undef; $self->{'restrictionSetId'} = undef; # delete old sessions $self->cleanupExpiredSessions(); # find em from persisted # Try to use cookies first, then fallback to CGI parameters. my $wsi = $self->getCookie(name => 'wsi'); if (defined($wsi)) { $self->setPersistedValue(name => "wsi", value => undef); } else { $wsi = $self->getPersistedValue( name=> "wsi" ); # We need to re-persist our session hash and identifier. $self->{'persist'} = 1; } if ( defined($wsi) && length($wsi) ) { $self->{'sessionId'} = Pos::web::digitize( dirty=> $wsi ); } else { return; # there's not a session to setup! } # Try to use cookies if a persisted value isn't defined my $wsh = $self->getCookie(name => "wsh"); if ( defined($wsh) && length($wsh) ) { $self->setPersistedValue(name => "wsh", value => undef); } else { $wsh = $self->getPersistedValue( name=> "wsh" ); } $self->{'sessionId'} = $wsi; $self->{'sessionHash'} = $wsh; my @si; if( (defined($self->{'sessionId'}) && length($self->{'sessionId'})) && (defined($self->{'sessionHash'}) && length($self->{'sessionHash'}))) { # now we verify that it is a valid session .. @si = $db->query( sql=> " select user_id from client_sessions where client_session_id = '" . $db->escape( dirty=> $self->{'sessionId'} ) . "' and upper(client_session_hash) = '" . $db->escape( dirty=> uc($self->{'sessionHash'}) ) . "' "); } if ( @si ) { # good session, save and persist, touch db $self->{'userId'} = $si[0]; if ($self->{'persist'}) { $self->setPersistedValue( name=> "wsi", value=> $self->{'sessionId'} ); $self->setPersistedValue( name=> "wsh", value=> $self->{'sessionHash'} ); } $db->do( sql=> " update client_sessions set touch_time = " . $db->getCurrentTimeStampString() . " where client_session_id = '" . $db->escape( dirty=> $self->{'sessionId'} ) . "'" ); } else { # in the future, we will be checking cookies to give a 2nd chance # no session or bad, undef sessionId and sessionHash $self->{'sessionId'} = undef; $self->{'sessionHash'} = undef; } $db->commit(); } sub getUserName { my $self = shift; my $db = $self->{'webtopDb'}; my $userName; if (defined $self->getUserId()) { ($userName) = $db->query(sql => " select user_name from users where user_id = " . $self->getUserId() . " "); } return $userName if defined $userName; return undef; } sub getCustomerId { my $self = shift; my $db = $self->{'webtopDb'}; if ( exists $self->{'customerId'} ) { return $self->{'customerId'}; } if ( not defined $self->{'userId'} ) { return undef; } ($self->{'customerId'}) = $db->query( sql => " SELECT customer_id FROM groups WHERE group_id = ( SELECT group_id FROM users WHERE user_id = " . $self->{'userId'} . " ) "); return $self->{'customerId'}; } sub createSession { my $self = shift; my ($userId, $userName, $passwd, $uslId, $a, $z) = @{{@_}}{qw/userId userName passwd uslId a z/}; my $isExpired = 0; my $trialExpired = 0; my $challenge = ''; my $db = $self->{'webtopDb'}; $self->{'userId'} = $userId; $self->{'userName'} = $userName; $self->{'passwd'} = $passwd; $self->{'uslId'} = $uslId; $self->{'a_param'} = $a; $self->{'z_param'} = $z; # Create the database hashes and what not. $self->createSessionHashes(); # persist the values also if ($self->{'persist'}) { $self->setPersistedValue( name=> "wsi", value=> $self->{'sessionId'} ); $self->setPersistedValue( name=> "wsh", value=> $self->{'sessionHash'} ); } $db->commit(); } sub createInitializedSession { my $self = shift; my ($userId, $customerId, $launcherSession) = @{{@_}}{qw/userId customerId launcherSession/}; $self->{'userId'} = $userId; $self->{'customerId'} = $customerId; $self->createSessionHashes(launcherSession => $launcherSession); return 1; } sub createSessionHashes { my $self = shift; my ($launcherSession) = @{{@_}}{qw/launcherSession/}; my $confidenceOnlinePassed = 0; my $db = $self->{'webtopDb'}; # Create id my ($newSessionId) = $db->getNextSequenceValue( sequence => "client_sessions_pk"); $self->{'sessionId'} = $newSessionId; # Create hash my $randStuff = rand(2**32) . rand(2**32); # two random numbers under 4 gig my $md5 = Digest::MD5->new(); $md5->add( $randStuff ); $self->{'sessionHash'} = $md5->hexdigest(); # Get remote address my $raddr = $ENV{'REMOTE_ADDR'}; $raddr =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/; $raddr = "$1.$2.$3.$4"; my $anxS = 0; if ($self->{'uslId'}) { ($anxS) = $db->query( sql => " select anx_session_id from anx_portal_sessions where anx_session_id = '" . $db->escape( dirty => $self->{'uslId'} ) . "' "); } my $ssoPass; if ($anxS) { ($ssoPass) = $db->query( sql => " select single_sigon_password from anx_portal_sessions where anx_session_id = " . $db->escape( dirty => $self->{'uslId'} ) . " "); $db->do( sql => " update anx_portal_sessions set client_session_id = $newSessionId, client_session_hash = '" . $self->{'sessionHash'} . "', a_param = '" . $db->escape( dirty => $self->{'a_param'} ) . "', z_param = '" . $db->escape( dirty => $self->{'z_param'} ) . "' where anx_session_id = " . $db->escape( dirty => $self->{'uslId'} ) . " "); } else { my ($cnt) = $db->query(sql=> " select count(hook_id) from w_user_single_signon_hooks where user_id = " . $self->{'userId'} . ""); if ($cnt) { $ssoPass = $self->{'passwd'}; } } $db->do( sql=> " insert into client_sessions ( client_session_id, client_session_hash, client_session_type_id, user_id, touch_time, connect_date, public_ip_address, single_signon_password ) values ( $newSessionId, '" . $self->{'sessionHash'} . "', 1, " . $self->{'userId'} . ", " . $db->getCurrentTimeStampString() . ", " . $db->getCurrentTimeStampString() . ", dotted_quad_to_rawip32('$raddr'), '" . $db->escape( dirty => $ssoPass ) . "' ) "); } sub setRestrictionSetId { my $self = shift; my ($tb) = @{{@_}}{qw/tb/}; if ( !defined($self->getRestrictionSetId()) || !length($self->getRestrictionSetId()) ) { $self->{'restrictionSetId'} = $self->checkActiveRestrictionSetId(tb=> $tb); $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP Client: Setting restriction set id to $self->{'restrictionSetId'} " ); } else { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP Client: Restriction set id is $self->{'restrictionSetId'} " ); } return $self->{'restrictionSetId'}; } sub getRestrictionSetId { return shift->{'restrictionSetId'}; } sub checkActiveRestrictionSetId { my $self = shift; my ($tb) = @{{@_}}{qw/tb/}; # we now need to check if they have been assigned a restriction set... my $cdb = $tb->getWebtopDbReader(); my $wsi = $self->getSessionId; my ($activeRestrictionSetId) = $cdb->query( sql=> "select restrict_set_id from client_sessions where client_session_id = '" . $cdb->escape( dirty=> $wsi ) . "' "); if ( !defined($activeRestrictionSetId) || !length($activeRestrictionSetId) ) { # the second thing we try is to call the external program that determines the restriction set # for us based on a web session. my $oldPath = $ENV{'PATH'}; $oldPath = "" unless defined $oldPath; my $oldLd = $ENV{'LD_LIBRARY_PATH'}; $oldLd = "" unless defined $oldLd; $ENV{'PATH'} = "/bin:/usr/bin"; # we need no path! $ENV{'LD_LIBRARY_PATH'} = "/usr/local/positive/Library:$oldLd"; my $wsiClean = $wsi; $wsiClean =~ s/(\W)/\\$1/g; my $command = "/usr/local/positive/Binary/WebtopDetermineActiveRestrictSetId $wsiClean"; $command =~ /^(.*)$/; $command = $1; $activeRestrictionSetId = `$command`; $ENV{'PATH'} = $oldPath; $ENV{'LD_LIBRARY_PATH'} = $oldLd; chomp $activeRestrictionSetId; if ( not ( defined($activeRestrictionSetId) && length($activeRestrictionSetId) && $activeRestrictionSetId =~ /^\d+$/ && $activeRestrictionSetId ne "0" ) ) { $activeRestrictionSetId = undef; } $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP Client: Tried the helper command for active restrict set id..." ); if ( not defined $activeRestrictionSetId ) { $self->logger( severity=>'ERROR', logMessage=>"WEBTOP Client: Could not determine the restriction set id to use for this session, giving up!" ); exit(1); } # now we update the current restriction set id my $db = $tb->getWebtopDbWriter(); $db->do( sql=> "update client_sessions set restrict_set_id = $activeRestrictionSetId where client_session_id = $wsi and activex_disabled = '1'" ); $db->commit(); $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP Client: using restriction set $activeRestrictionSetId" ); } return $activeRestrictionSetId; } sub usingOcx { my $self = shift; my ($tb) = @{{@_}}{qw/tb/}; my $db = $tb->getWebtopDbWriter(); my $webSessionId = $self->getSessionId(); $webSessionId =~ /^(\d+)$/; $webSessionId = $1; $db->do(sql => " update client_sessions set activex_disabled = 0 where client_session_id = '$webSessionId'"); $db->commit(); } sub notUsingOcx { my $self = shift; my ($tb) = @{{@_}}{qw/tb/}; my $db = $tb->getWebtopDbWriter(); my $webSessionId = $self->getSessionId(); $webSessionId =~ /^(\d+)$/; $webSessionId = $1; $db->do(sql => " update client_sessions set activex_disabled = 1 where client_session_id = '$webSessionId'"); $db->commit(); } # Checks to see if the OCX is required. If it is not, the page may redirect # to the main.pl when it fails to load. sub isOcxRequired { my $self = shift; my ($tb, $ocxFailed) = @{{@_}}{qw/tb ocxFailed/}; my $userId = $self->getUserId(); my $db = $tb->getWebtopDbWriter(); my $rid; # If the ocx failed, flag that in the database if ($ocxFailed) { my $webSessionId = $self->getSessionId(); $webSessionId =~ /^(\d+)$/; $webSessionId = $1; $db->do(sql => " update client_sessions set activex_disabled = 1 where client_session_id = '$webSessionId'"); $db->commit(); } $rid = $self->checkActiveRestrictionSetId(tb => $tb); # If any of the following captive portal deciders has an entry for this user and this restriction set # we must assume that the ActiveX control is required. If the admin did not want to require portalization # for this user he would create a restriction set tab that had these requirements set to no. my ($isRequired) = $db->query(sql => " select 1 " . ((Pos::defaults::isPostgresDb()) ? "" : " from dual ") . " where (( select enforce from user_virus_enforce where user_id = '$userId' and restrict_set_id = '$rid' ) = 1 AND ( select enforce from w_user_enforce_antivirus where user_id = '$userId' and enforce = 1 ) = 1) OR (( select require_distribute from user_spyware_enforce where user_id = '$userId' and restrict_set_id = '$rid' ) = 1 AND ( select enforce from w_user_enforce_spyware where user_id = '$userId' and enforce = 1 ) = 1) OR (( select is_required from user_critical_updates_required where user_id = '$userId' and restrict_set_id = '$rid' ) = 1 AND ( select enforce from w_user_enforce_crit_updates where user_id = '$userId' and enforce = 1 ) = 1) OR (( select enforce from w_user_enforce_appdist where user_id = '$userId' and enforce = 1 ) = 1 AND ( select enforce from user_appdist_enforce where user_id = $userId AND restrict_set_id = $rid ) = 1) "); $isRequired = 0 if (not defined($isRequired)); $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP Client: OCX is" . ($isRequired ? "" : " not") . " required, using rid $rid" ); return $isRequired; # my $agent = $ENV{'HTTP_USER_AGENT'}; # my $required = 1; # # if (!($agent =~ /MSIE/)) { # $required = 0; # } # # return $required; # return 0; # Right now, as there is no policy, we do not require the ActiveX control to load for ANY browser. } # # Checks to see if a session is captive portalized # # First, it checks to see if the session should be worry about # being captive portalized # sub isCaptivePortalized { my $self = shift; my ($tb) = @{{@_}}{qw/tb/}; my $requiresCaptivePortal = 1; my $portalized = 1; my $db = $self->{'webtopDb'}; my $sessionId; my $agent = $ENV{'HTTP_USER_AGENT'}; # Use the isOcxRequired method to tell us whether or not we need to use the captive portal $requiresCaptivePortal = $self->isOcxRequired(tb => $tb); # If captive portal is not required for this session, return that the session # is not captive portalized. if (!$requiresCaptivePortal) { return 0; } # Now check to see if the session really is captive portalized $sessionId = $db->escape(dirty => $self->{'sessionId'}); if (defined($sessionId)) { my ($numPortals) = $db->query(sql => " select count(*) from captive_portalized cs, client_sessions us where cs.user_id = us.user_id AND cs.computer_id = us.computer_id AND us.client_session_id = '" . $db->escape( dirty=> $sessionId ) . "' "); # If the query returns no rows, the session is not portalized. if (not defined($numPortals) or $numPortals == 0) { $portalized = 0; } else { $portalized = 1; } } else { # XXX: Re-evalulate this -- should a user be let out of the captive # portal if their user session identifier cannot be determined? # This seems like a job for restriction sets. $portalized = 0; } if ($portalized) { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: User is captive portalized" ); } return $portalized; } sub isLauncherSession { return 0; } # # Checks to see if a user's password is expired after they have authenticated. # A better way to implement this would be to have the radius server return whether or # not the password was expired. # sub isPasswordExpired { my $self = shift; return $self->{'expired'}; } sub isTrialExpired { my $self = shift; return $self->{'trialExpired'}; } sub challenge { my $self = shift; return $self->{'challenge'}; } sub requiredToChangePassword { my $self = shift; my $db = $self->{'webtopDb'}; my $pwResetRequired; if ($self->{'userId'}) { my ($authType) = $db->query( sql=> " select upper(auth_type_name) from auth_types at, user_auth_types uat where at.auth_type_id = uat.auth_type_id and user_id = '$self->{'userId'}' "); if ($authType ne 'SECURID' && $authType ne 'PHONEFACTOR - SECURID') { if (!$self->{'expired'}) { ($pwResetRequired) = $db->query( sql => " select ext_password_expired from users where user_id = '$self->{'userId'}' "); } } $pwResetRequired and $self->{'expired'} = 1; } return $self->{'expired'}; } sub getUserId { return shift->{'userId'}; } # warning, setUserId should only be used when you want # to forge a client object for backend scripts and arne't # really online in the webtop sub setUserId { my $self = shift; my ($userId) = @{{@_}}{qw/userId/}; $self->{'userId'} = $userId; } sub getSessionParams { my $self = shift; if ($self->{'persist'}) { return "pVwsi=" . HTML::Entities::encode($self->getSessionId()) . "&pVwsh=" . HTML::Entities::encode($self->getSessionHash()); } else { return ""; } } sub getSessionId { return shift->{'sessionId'}; } sub getSessionHash { return shift->{'sessionHash'}; } # we override the base class getFormGetCode and getFormInputs to do the following: # if not persist, then we manually tack on the session vars that MUST be sent # for the Pos::Webtop::Web::Client object. We then call the base class methods # based on that logic. sub getFormGetCode { my $self = shift; my ($persist,$values) = @{{@_}}{qw/persist values/}; # base class says that it is true by default, so we do that also $persist = 1 unless defined $persist; if ( not $persist ) { my %args = @_; # this is the callers param list (copy) # we need to call base class method with our stuff tacked on # note that we will make a copy and replace their parameter with ours... # if we just modified the $values refed hash, we might screw up the calling # context's data. # We only do this if we need to persist the session information. if ($self->{'persist'}) { my $persistPrefix = $self->getPersistPrefix(); my $newValues = [ { name=> $persistPrefix . "wsi", value=> $self->{'sessionId'} }, { name=> $persistPrefix . "wsh", value=> $self->{'sessionHash'} } ]; # put their values in with ours... push @{ $newValues }, @{ $values }; # modify our copy of the args and replace the values hash entry # with our new values list... $args{'values'} = $newValues; } else { $args{'values'} = $values; } # now call the base class method with new args hash return $self->Pos::Web::Client::getFormGetCode( %args ); } else { # just call base class method since we persisted the wsi and wsh vars return $self->Pos::Web::Client::getFormGetCode(@_); } } sub getFormInputs { my $self = shift; my ($persist,$values) = @{{@_}}{qw/persist values/}; # base class says that it is true by default, so we do that also $persist = 1 unless defined $persist; if ( not $persist ) { # we need to call base class method with our stuff tacked on # note that we will make a copy and replace their parameter with ours... # if we just modified the $values refed hash, we might screw up the calling # context's data. my $persistPrefix = $self->getPersistPrefix(); my %args = @_; # this is the callers param list (copy) my $newValues = [ { name=> $persistPrefix . "wsi", value=> $self->{'sessionId'} }, { name=> $persistPrefix . "wsh", value=> $self->{'sessionHash'} } ]; # put their values in with ours... push @{ $newValues }, @{ $values }; # modify our copy of the args and replace the values hash entry # with our new values list... $args{'values'} = $newValues; # now call the base class method with new args hash return $self->Pos::Web::Client::getFormInputs( %args ); } else { # just call base class method since we persisted the wsi and wsh vars return $self->Pos::Web::Client::getFormInputs(@_); } } sub checkSession { my $self = shift; my ($ignoreAuxAuth) = @{{@_}}{qw/ignoreAuxAuth/}; my $db = $self->{'webtopDb'}; my $userId = $self->getUserId(); my @si; my @badAuxAuths; $ignoreAuxAuth = 0 unless defined $ignoreAuxAuth; $userId = 0 unless defined($userId); if( (defined($self->{'sessionId'}) && length($self->{'sessionId'})) && (defined($self->{'sessionHash'}) && length($self->{'sessionHash'}))) { # now we verify that it is a valid session .. @si = $db->query( sql=> " select user_id from client_sessions where client_session_id = '" . $db->escape( dirty=> $self->{'sessionId'} ) . "' and upper(client_session_hash) = '" . $db->escape( dirty=> uc($self->{'sessionHash'}) ) . "' "); if (!$ignoreAuxAuth && scalar(@si)) { @badAuxAuths = $db->query(sql=> " select aux_auth_id from client_session_aux_auths where client_session_id = '" . $db->escape( dirty=> $self->{'sessionId'} ) . "' and (passed = false or expired = true)"); } } !$userId and $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: checkSession() - No userid for some reason." ); !scalar(@si) and $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: checkSession() - session has and session id not found in client_sessions table...could have expired." ); scalar(@badAuxAuths) and $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: checkSession() - Haven't authed with " . scalar(@badAuxAuths) . " aux auth." ); if ( !$userId || !scalar(@si) || scalar(@badAuxAuths)) { # invalid session # redir to login.pl and exit print "Content-type: text/html\n\n"; print " "; exit(); } } sub cleanupExpiredSessions { my $self = shift; my $db = $self->{'webtopDb'}; my $cleaned = $self->{'cleaned'}; # Only do this once per instantiation if (defined($cleaned) and $cleaned == 1) { return; } $db->do(sql => " delete from client_sessions where touch_time < " . $db->getCurrentTimeStampString() . " - interval '1 hour' and concentrator_session_id is null and client_session_type_id = 1 "); $db->do(sql => " delete from anx_portal_sessions where client_session_id is not null and client_session_id not in ( select client_session_id from client_sessions ) "); $db->commit; $self->{'cleaned'} = 1; } sub getUserContentModules { my $self = shift; # # get the hash with the module access info # my $allowedHash = $self->getAllowedContentModules(); my $intranet = $allowedHash->{'Intranet'}->{'m_intranet'}; my $applications = $allowedHash->{'Applications'}->{'m_applications'}; my $email = $allowedHash->{'Mail'}->{'m_email'}; my $fileShares = $allowedHash->{'FileShares'}->{'m_fileshares'}; my $remoteDesktop = $allowedHash->{'RemoteDesktop'}->{'m_remoteDesktop'}; # # so i can do the foreach down there # my @blah = ($intranet,$applications,$email,$fileShares,$remoteDesktop); # # get the number of modules that the user can see, # if it is 2 then we have to assign the col and rows # different so they can be side by side instead of on # top of each other # my $numberOfModules = 0; foreach my $module ( @blah ) { $module and $numberOfModules++; } # # these are the col and row numbers, hardcoded # for 5 modules cause i couldnt figure out a good # way not to hard code it...i wasted so much time on that # my @col; my @row; my @moduleOrder = ("0,0",($numberOfModules < 3 ? "1,0" : "0,1"),"1,0","1,1","1,2"); my @linkOrder; my $i = 0; # # if the module is ye then assign it a col, row number and link order number # else # asign it x # foreach my $yeOrNe ( @blah ) { if ($yeOrNe) { my ($orderCol,$orderRow) = split(/,/,$moduleOrder[$i]); push @col, $orderCol; push @row, $orderRow; push @linkOrder, $i; $i++; } else { push @col, "x"; push @row, "x"; push @linkOrder,"x"; } } my $cmi = { Intranet=> { moduleName=> "Intranet", shown=> $intranet, shownMobile=> 1, col=> $col[0], row=> $row[0], icon=> "intranetIcon.png", linkOrder=> $linkOrder[0], requiresActiveX=> 0 }, Applications=> { moduleName=> "Applications", shown=> $applications, shownMobile=> 0, col=> $col[1], row=> $row[1], icon=> "applicationsIcon.png", linkOrder=> $linkOrder[1], requiresActiveX=> 1 }, Mail=> { moduleName=> "Email", shown=> $email, shownMobile=> 1, col=> $col[2], row=> $row[2], icon=> "mailIcon.png", linkOrder=> $linkOrder[2], requiresActiveX=> 0 }, FileShares=> { moduleName=> "File Shares", shown=> $fileShares, shownMobile=> 1, col=> $col[3], row=> $row[3], icon=> "fileSharesIcon.png", linkOrder=> $linkOrder[3], requiresActiveX=> 0 }, RemoteDesktop=> { moduleName=> "Remote Desktop", shown=> $remoteDesktop, shownMobile=> 0, col=> $col[4], row=> $row[4], icon=> "remoteDesktopIcon.png", linkOrder=> $linkOrder[4], requiresActiveX=> 0 }, UserPrefs=> { moduleName=> "Preferences", shown=> 1, shownMobile=> 0, col=> "", row=> "", linkOrder=> "", icon=> "", noPreview => 1 } }; return $cmi; } sub getAllowedContentModules { my $self = shift; my $db = $self->{'webtopDb'}; my $tb = Pos::Webtop::Toolbox->new(); # # get the modules and sub modules access info # # m_* is the actual module # sm_* is a submodule of module * or something # my @allowed = $db->query(sql => " select m_f_fileshares, sm_f_new_fileshare, m_i_intranet, m_a_applications, sm_a_user_defined_atp, sm_a_app_launcher, m_e_email, m_rd_remote_desktop, sm_rd_manually_connect, sm_rd_install_agent from w_user_module_access where user_id = '" . $self->getUserId() . "' and restrict_set_id = '" . $self->getRestrictionSetId() . "' "); # # create mad hash of module access info # my $moduleAccess = { Intranet=> { m_intranet=> ($allowed[0][2] || !defined($allowed[0][2]) ? "1" : "0") }, Applications=> { m_applications=> ( ($allowed[0][3] || !defined($allowed[0][3])) && $self->isWebtopVpnConnectionEstablished(tb=> $tb, db=>$db) ? "1" : "0"), sm_userDefinedAtp=> ($allowed[0][4] || !defined($allowed[0][4]) ?"1" : "0"), sm_appLauncher=> ($allowed[0][5] || !defined($allowed[0][5]) ? "1" : "0") }, Mail=> { m_email=> ($allowed[0][6] || !defined($allowed[0][6]) ? "1" : "0") }, FileShares=> { m_fileshares=> ($allowed[0][0] || !defined($allowed[0][0]) ? "1" : "0"), sm_newFileshare=> ($allowed[0][1] || !defined($allowed[0][1]) ? "1" : "0") }, RemoteDesktop=> { m_remoteDesktop=> ( ($allowed[0][7] || !defined($allowed[0][7])) && ($self->isWebtopVpnConnectionEstablished(tb=> $tb, db=>$db) || ( $allowed[0][9] || !defined($allowed[0][9]) )) && !$self->isMacWebtop() ? "1" : "0"), sm_manuallyConnect=> ($allowed[0][8] || !defined($allowed[0][8]) ? "1" : "0"), sm_installAgent=> ($allowed[0][9] || !defined($allowed[0][9]) ? "1" : "0") } }; return $moduleAccess; } sub isSupportAccount { my $self = shift; my ($db, $userId) = @{{@_}}{qw/db userId/}; my $supportAccount = 0; if ( defined($userId) ) { ($supportAccount) = $db->query(sql => " select count(user_id) from users where user_id = $userId AND is_support_account = 1 "); } if ( $supportAccount ) { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: This user is set as the support account." ); } return $supportAccount; } sub isSupportAccountActive { my $self = shift; my ($db, $userId) = @{{@_}}{qw/db userId/}; my $activeSupportAccount = 0; if ( defined($userId) ) { ($activeSupportAccount) = $db->query(sql => " select count(user_id) from users where user_id = $userId AND is_support_account = 1 AND " . $db->getCurrentDateString() . " < last_password_reset " . ((Pos::defaults::isPostgresDb()) ? "+ interval '1 hour'" : "+ 1/24" ) . " "); } if ( $activeSupportAccount ) { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: Support account is active." ); } else { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: Support account has expired." ); } return $activeSupportAccount; } sub isMobileWebtop { my $self = shift; my $agent = $ENV{'HTTP_USER_AGENT'}; $agent = uc($agent); if (($agent =~ s/WINDOWS CE/$agent/ || $agent =~ s/NETFRONT/$agent/ || $agent =~ s/PALM OS/$agent/ || $agent =~ s/BLAZER/$agent/ || $agent =~ s/ELAINE/$agent/ || $agent =~ s/^WAP.*$/$agent/ || $agent =~ s/PLUCKER/$agent/ || $agent =~ s/PPC/$agent/ || $agent =~ s/SMARTPHONE/$agent/ || $agent =~ s/IEMOBILE/$agent/ || $agent =~ s/SYMBIAN/$agent/ || $agent =~ s/BLACKBERRY/$agent/ || $agent =~ s/OPERA MINI/$agent/ || $agent =~ s/OPWV/$agent/ || $agent =~ s/ADVANTGO/$agent/) && $agent !~ s/MACINTOSH/$agent/ ) { return 1; } else { return 0; } } # # this will return 1 if it is a handheld pc # with something other than windows ce # sub isLameHandheldThatSux { my $self = shift; my ($dow) = @{{@_}}{qw/dow/}; if ($dow) { return 1; } else { return 0; } } sub isLameUsingOwa { my $self = shift; my ($db, $userId) = @{{@_}}{qw/db userId/}; my @res = $db->query(sql => " select wes.email_setting_name from w_email_settings wes, w_user_email_settings wues where wues.email_setting_id = wes.email_setting_id AND wues.user_id = '$userId' "); if (scalar(@res) == 0 || $res[0] ne 'OWA') { return 0; } else { return 1; } } sub getOwaServer { my $self = shift; my ($db, $userId) = @{{@_}}{qw/db userId/}; my @res = $db->query(sql => " select owa_server from w_user_email_settings_owa where user_id = '$userId' "); if (scalar(@res) == 0) { return 0; } else { return $res[0]; } } sub isWebtopVpnConnectionEstablished { my $self = shift; my ($db) = @{{@_}}{qw/db/}; my ($vpnConnection) = $db->query(sql => " select control_session_id from client_sessions where client_session_id = '" . $db->escape( dirty=>$self->getSessionId() ) . "' and client_session_type_id = (select client_session_type_id from client_session_types where upper(client_session_type_name) = 'WEBTOP') "); if($vpnConnection) { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: Webtop VPN connection is established" ); return 1; } $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: Webtop VPN connection is not established for user" ); return 0; } sub isActivexDisabled { my $self = shift; my ($db) = @{{@_}}{qw/db/}; my ($activexDisabled) = $db->query(sql => " select activex_disabled from client_sessions where client_session_id = '" . $db->escape( dirty=> $self->getSessionId() ) . "' "); if ($activexDisabled) { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECUIRTY CHECKS: ActiveX is disabled for user" ); } return $activexDisabled; } sub isPassedCaptivePortalChecks { my $self = shift; my ($db) = @{{@_}}{qw/db/}; my ($hasPassedChecks) = $db->query(sql => " select webtop_passed_checks from client_sessions where client_session_id = '" . $db->escape( dirty=>$self->getSessionId() ) . "' "); if (!$hasPassedChecks || not defined($hasPassedChecks)) { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: User has not passed all captive portal checks" ); } else { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: User has passed all captive portal checks" ); } return $hasPassedChecks; } sub isPortalzOpen { my $self = shift; my ($db) = @{{@_}}{qw/db/}; my ($numPortals) = $db->query(sql => " select count(*) from captive_portalized cs, client_sessions us where cs.user_id = us.user_id AND cs.computer_id = us.computer_id AND us.client_session_id = '" . $db->escape( dirty => $self->getSessionId() ) . "' "); if (not scalar($numPortals)) { warn "WEBOP SECURITY CHECKS: $numPortals portals open for user " . $self->getUserName() . "\n"; } return $numPortals; } # # checks to make sure the user didn't # get around the portal checks for ocx # and stuff # sub isOcxRequiredCaptivePortalCheck { my $self = shift; my ($db, $tb) = @{{@_}}{qw/db tb/}; not defined($tb) and $tb = Pos::Webtop::Toolbox->new(); my $hasPassedChecks = $self->isPassedCaptivePortalChecks(db=>$db); $hasPassedChecks and return [0,0,1]; if ($self->isOcxRequired(tb => $tb)) { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECK: OCX is required for user, checking for portals and activex" ); my $portalized; my $numPortals = $self->isPortalzOpen(db=>$db); my $activexDisabled = $self->isActivexDisabled(db=>$db); not defined($activexDisabled) and $activexDisabled = 0; my $vpnConnection = $self->isWebtopVpnConnectionEstablished(db=>$db); # If the query returns no rows, the session is not portalized. if ((not defined($numPortals) or $numPortals == 0) && $activexDisabled == 0) { $portalized = 0; } else { $portalized = 1; } return [$portalized,$activexDisabled,$vpnConnection]; } # if it gets here that means they are able to login so they are kew for this session $db->do(sql => " update client_sessions set webtop_passed_checks = '1' where client_session_id = '" . $self->getSessionId() . "' "); $db->commit; return [0,0,1]; } sub isWebtopAccessEnabled { my $self = shift; my ($tb, $db) = @{{@_}}{qw/tb db/}; my $userId = $self->getUserId(); my ($userHasAccess) = $db->query( sql => " select can_use_webtop from user_webtop_access where user_id = '$userId' and can_use_webtop = 1 and restrict_set_id = '" . $self->getRestrictionSetId() . "' limit 1 "); if (not defined($userHasAccess)) { $userHasAccess = 0; $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: Webtop access is disabled for user" ); } return $userHasAccess; } sub isUserRegistered { my $self = shift; my ($tb, $db) = @{{@_}}{qw/tb db/}; my $userId = $self->getUserId(); my ($userHasRegistered) = $db->query(sql => " select case when c.trial_expiration_date is null then u.register_complete else 1 end as register_complete from users u inner join groups g on u.group_id = g.group_id inner join customers c on g.customer_id = c.customer_id where u.user_id = '$userId' "); if (not defined($userHasRegistered)) { $userHasRegistered = 0; $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: User has not gone through the registration stuff" ); } return $userHasRegistered; } sub isUserTryingToBeSneaky { my $self = shift; my ($tb, $db) = @{{@_}}{qw/tb db/}; my $userId = $self->getUserId(); my $registered = 0; my $isSupportAccount = $self->isSupportAccount( db=> $db, userId=> $userId ); my $hasPassedChecks = $self->isPassedCaptivePortalChecks(db=>$db); $hasPassedChecks and return 0; !$isSupportAccount and $registered = $self->isUserRegistered( tb=> $tb, db=> $db ); my $webtopAccess = $self->isWebtopAccessEnabled( tb=> $tb, db=> $db ); $isSupportAccount and $registered = 1; if(!$registered || !$webtopAccess) { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECK: Looks like user is trying to be sneaky" ); return 1; } return 0; } sub isVpnAccessEnabled { my $self = shift; my ($db, $userId) = @{{@_}}{qw/db userId/}; my ($vpnAccess) = $db->query( sql => " select can_use_vpn | can_use_l2tp from user_vpn_access where user_id = '$userId' and (can_use_vpn = 1 OR can_use_l2tp = 1) limit 1 "); my ($vpnDownload) = $db->query( sql => " select can_download_vpn | can_download_l2tp_ios | can_download_l2tp_macosx from user_vpn_access where user_id = '$userId' limit 1 "); if(not defined($vpnAccess)) { $vpnAccess = 0; $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: VPN access for user is disabled" ); } if(not defined($vpnDownload)) { $vpnDownload = 0; $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: VPN client dowload for user is disabled" ); } return [$vpnAccess,$vpnDownload]; } sub getUserType { my $self = shift; my ($db, $userId) = @{{@_}}{qw/db userId/}; my ($userType) = $db->query(sql => " select user_type_name from user_types ut, users u WHERE u.user_id = '$userId' and u.user_type_id = ut.user_type_id "); not defined($userType) and $userType = 0; return $userType; } sub canUserChangePassword { my $self = shift; my ($db,$userId) = @{{@_}}{qw/db userId/}; my ($canUserChangePassword) = $db->query(sql => " select can_change_password from users where user_id = $userId "); if ( $canUserChangePassword ) { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: User password change'n has been enabled." ); } else { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: User password change'n has been disabled." ); } return $canUserChangePassword; } sub getMacWebtopVersion { my $self = shift; my ($db, $userId) = @{{@_}}{qw/db userId/}; my $releaseId; # Note that this is a different query than the one in the OCX version # checker. This one's last query checks stable, not latest. (not to mention # actually uses the result too). # my @info = $db->query(sql => " select ( select software_release_id from user_software_versions where user_id = '$userId' AND software_product_id = sp.software_product_id ) as USER_SOFTWARE_ACTUAL_RELEASE, ( select spb.software_release_id from software_product_branches spb, user_software_versions usv where spb.software_branch_type_id = usv.software_branch_type_id AND usv.software_product_id = sp.software_product_id AND spb.software_product_id = usv.software_product_id AND usv.user_id = '$userId' ) as USER_SOFTWARE_BRANCH_RELEASE, ( select software_release_id from software_product_branches where software_branch_type_id = ( select software_branch_type_id from software_branch_types where upper(software_branch_type_name) = upper('stable') ) AND software_product_id = sp.software_product_id ) as GLOBAL_SOFTWARE_RELEASE from software_products sp where sp.distribution_identifier = 'macwebtop' "); my ($userActualRelease, $userBranchRelease, $globalRelease) = @{ $info[0] }; if (defined($userActualRelease)) { $releaseId = $userActualRelease; } elsif (defined($userBranchRelease)) { $releaseId = $userBranchRelease; } elsif (defined($globalRelease)) { $releaseId = $globalRelease; } if (! (defined($releaseId) && length($releaseId))) { return [62,0]; } # This query should already exist as an sproc -- I left it the same so # the sproc could be reused, even though we don't need activex_version. # @info = $db->query(sql => " select major_release_number, minor_release_number, activex_version from software_releases where software_release_id = '$releaseId' "); if (! @info) { return [62,0]; } my ($major, $minor, $activeXVersion) = @{ $info[0] }; return [$major,$minor]; } sub getWebtopOcxVersion { my $self = shift; my ($db, $userId) = @{{@_}}{qw/db userId/}; my $releaseId; my @info = $db->query(sql => " select ( select software_release_id from user_software_versions where user_id = '$userId' AND software_product_id = sp.software_product_id ) as USER_SOFTWARE_ACTUAL_RELEASE, ( select spb.software_release_id from software_product_branches spb, user_software_versions usv where spb.software_branch_type_id = usv.software_branch_type_id AND usv.software_product_id = sp.software_product_id AND spb.software_product_id = usv.software_product_id AND usv.user_id = '$userId' ) as USER_SOFTWARE_BRANCH_RELEASE, ( select software_release_id from software_product_branches where software_branch_type_id = ( select software_branch_type_id from software_branch_types where upper(software_branch_type_name) = upper('latest') ) AND software_product_id = sp.software_product_id ) as GLOBAL_SOFTWARE_RELEASE from software_products sp where sp.distribution_identifier = 'webtop' "); my ($branch) = $db->query(sql => " select sbt.software_branch_type_name from user_software_versions usv, software_product_branches spb, software_branch_types sbt where usv.user_id = '$userId' AND usv.software_branch_type_id = sbt.software_branch_type_id "); my ($userActualRelease, $userBranchRelease, $globalRelease) = @{ $info[0] }; if (defined($userActualRelease)) { $releaseId = $userActualRelease; } elsif (defined($userBranchRelease)) { $releaseId = $userBranchRelease; } @info = $db->query(sql => " select major_release_number, minor_release_number, activex_version from software_releases where software_release_id = '$releaseId' "); my ($major, $minor, $activeXVersion) = @{ $info[0] }; # if it isn't defined then we default to a version that is old... # works like this...if we are currently on 2,1,23,5 but the policy # is set to 2,1,23,1 and the user does not have the activex installed # it will grab the current cab and install it...the next time the user # logs in and if the policy is still set to 2,1,23,1 ie will relize that # it has a newer version of the activex installed and will not attempt to # upgrade and will just login...if the policy gets set to 2,1,23,6 then # (admin) ie will upgrade the activex (non-admin) webtop will tell the user # that the browser doesn't support activex not defined($activeXVersion) and $activeXVersion = "2,1,23,1"; return [$major,$minor,$activeXVersion]; } sub logger { my $self = shift; my $userName = $self->getUserName(); my ($severity,$logMessage) = @{{@_}}{qw/severity logMessage/}; warn "[" . localtime() . "] (" . uc($severity) . ") <" . (defined($userName) ? $userName : "USERNAME NOT AVAILABLE" ) . "> $logMessage \n"; return 1; } # This is simply a test that we are in a browser that is mac webtop enhanced # capable. This is intended to be used by areas of code that need to disable # enhanced features that are not available on mac yet. # sub isMacWebtop { my $agent = $ENV{'HTTP_USER_AGENT'}; my $ret = 0; if($agent =~ /Mac OS/) { if($agent =~ /Safari/ && $agent !~ /Chrome/) { $ret = 1; } elsif($agent =~ /Firefox/) { $ret = 1; } elsif($agent =~ /Chrome/) { $ret = 1; } } return $ret; } # RP - A new subroutine to determine if OS X access is enabled sub isOsxAccessEnabled { my $self = shift; my ($db, $userId) = @{{@_}}{qw/db userId/}; my $vpnAccess = 0; ($vpnAccess) = $db->query( sql => " select can_use_l2tp from user_vpn_access where user_id = '$userId' limit 1 "); if(not defined($vpnAccess)) { $self->logger( severity=>'NORMAL', logMessage=>"WEBTOP SECURITY CHECKS: VPN access for user is disabled" ); } return $vpnAccess; } 1;