aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey Armstrong <jeff@approximatrix.com>2021-06-29 16:06:44 -0400
committerJeffrey Armstrong <jeff@approximatrix.com>2021-06-29 16:06:44 -0400
commitcd4a5891b8e2a019fdfda7512d6490e075628292 (patch)
tree8012dc7dc52f17632f5b429dea79709e55ab96c4
parentd282a7e283a1704cdde2649c7caf130fb923051a (diff)
downloadlevitating-cd4a5891b8e2a019fdfda7512d6490e075628292.tar.gz
levitating-cd4a5891b8e2a019fdfda7512d6490e075628292.zip
Tokens are now checked for file uploads. Fixed major buffer bugs in status reporting.
-rw-r--r--captain/api.f9026
-rw-r--r--captain/db.f9020
-rw-r--r--captain/external.f9016
-rw-r--r--captain/gemini.f9015
-rw-r--r--captain/levitating-captain.prj3
-rw-r--r--captain/queryutils.f9012
-rw-r--r--captain/response.f902
-rw-r--r--captain/security.f90107
-rw-r--r--common/protocol.f9010
-rw-r--r--player/instructions.f906
-rw-r--r--player/player.F908
-rw-r--r--player/talking.f905
-rw-r--r--player/tasks.f9015
13 files changed, 223 insertions, 22 deletions
diff --git a/captain/api.f90 b/captain/api.f90
index 4d9545e..7b99ec7 100644
--- a/captain/api.f90
+++ b/captain/api.f90
@@ -182,6 +182,7 @@ contains
use server_response
use special_filenames
use logging
+ use security, only: validate_token => validate_titan_token
implicit none
type(titan_request), intent(in)::req
@@ -200,20 +201,25 @@ contains
! Task - "/api/player/{name}/job/{jobid}/task/{task num}"
if(trim(req%component(2)) == "player" .and. &
trim(req%component(4)) == "job" .and. &
- trim(req%component(6)) == "task") then
+ trim(req%component(6)) == "task") &
+ then
- job_id = req%path_component_int(5)
+ if(validate_token(req%token, req%component(3))) then
+
+ job_id = req%path_component_int(5)
- task_num = req%path_component_int(7)
+ task_num = req%path_component_int(7)
- write(job_text, '(I6)') job_id
- write(task_text, '(I6)') task_num
-
- call write_log("Handling a task update for job "//trim(job_text)//" task "//trim(task_text), LOG_INFO)
- call handle_task_request(req)
+ write(job_text, '(I6)') job_id
+ write(task_text, '(I6)') task_num
+
+ call write_log("Handling a task update for job "//trim(job_text)//" task "//trim(task_text), LOG_INFO)
+ call handle_task_request(req)
+
+ fullpath => get_task_result_static_filename(job_id, task_num)
+
+ end if
- fullpath => get_task_result_static_filename(job_id, task_num)
-
end if
if(associated(fullpath)) then
diff --git a/captain/db.f90 b/captain/db.f90
index 1f9bc01..448c17d 100644
--- a/captain/db.f90
+++ b/captain/db.f90
@@ -146,6 +146,26 @@ contains
end subroutine update_player_token_db
+ subroutine get_player_token_db(name, token)
+ implicit none
+
+ character(*), intent(in)::name
+ character(*), intent(out)::token
+ type(sqlite3_stmt)::stmt
+
+ token = " "
+
+ if(stmt%prepare(db, "SELECT token FROM players WHERE name=?") == SQLITE_OK) then
+ if(stmt%bind_text(1, name) == SQLITE_OK ) then
+ if(stmt%step() == SQLITE_ROW) then
+ call stmt%column_text(0, token)
+ end if
+ end if
+ end if
+ call stmt%finalize()
+
+ end subroutine get_player_token_db
+
function player_has_token_db(name)
implicit none
diff --git a/captain/external.f90 b/captain/external.f90
index edbe997..f5ba409 100644
--- a/captain/external.f90
+++ b/captain/external.f90
@@ -685,19 +685,33 @@ contains
function external_request_titan(req) result(resp)
use server_response
use special_filenames
+ use query_utilities
+ use security
use logging
implicit none
type(titan_request), intent(in)::req
type(response)::resp
+ type(query)::q
+
character(len=:), pointer::fullpath
character(12)::job_text, task_text
integer::job_id, task_num
+ logical::proceed_to_create_filename
+
character(64)::msg
- fullpath => get_full_filename_from_request(req)
+ fullpath => null()
+
+ proceed_to_create_filename = .false.
+
+ proceed_to_create_filename = validate_titan_token(req%token)
+
+ if(proceed_to_create_filename) then
+ fullpath => get_full_filename_from_request(req)
+ end if
if(associated(fullpath)) then
diff --git a/captain/gemini.f90 b/captain/gemini.f90
index ff83554..6070dbe 100644
--- a/captain/gemini.f90
+++ b/captain/gemini.f90
@@ -32,6 +32,7 @@ contains
subroutine read_request(ssl, req)
use jessl, only: ssl_read
use iso_c_binding
+ use logging
implicit none
type(c_ptr)::ssl
@@ -44,8 +45,9 @@ contains
req = " "
i = 1
-
+ call sleep(1)
bufread = ssl_read(ssl, buf)
+
do while(bufread > 0)
do j = 1, bufread
@@ -60,6 +62,11 @@ contains
end do
+ if(j > bufread) then
+ j = bufread
+ end if
+
+
if(buf(j) == c_new_line) then
exit
end if
@@ -67,6 +74,8 @@ contains
bufread = ssl_read(ssl, buf)
end do
+ call write_log("Request Read: "//trim(req), LOG_DEBUG)
+
end subroutine read_request
function read_into_buffer(unit_number, buffer)
@@ -288,6 +297,7 @@ contains
call req%path_component(1, first)
if(trim(first) == 'api') then
call write_log("API call encountered", LOG_DEBUG)
+
if(req%protocol == "gemini") then
resp = api_request_gemini(req)
call write_log("resp filename is: '"//trim(resp%body_filename)//"'", LOG_DEBUG)
@@ -295,13 +305,16 @@ contains
call treq%init_from_request(req, ssl)
resp = api_request_titan(treq)
end if
+
else
+
if(req%protocol == "gemini") then
resp = external_request_gemini(req)
else if(req%protocol == "titan") then
call treq%init_from_request(req, ssl)
resp = external_request_titan(treq)
end if
+
end if
call write_log("Handling response", LOG_DEBUG)
diff --git a/captain/levitating-captain.prj b/captain/levitating-captain.prj
index 392ac52..0e786f6 100644
--- a/captain/levitating-captain.prj
+++ b/captain/levitating-captain.prj
@@ -113,6 +113,9 @@
"filename":"response.f90",
"enabled":"1"
},{
+ "filename":"security.f90",
+ "enabled":"1"
+ },{
"filename":"special.f90",
"enabled":"1"
},{
diff --git a/captain/queryutils.f90 b/captain/queryutils.f90
index 550a2f3..00a914b 100644
--- a/captain/queryutils.f90
+++ b/captain/queryutils.f90
@@ -48,6 +48,7 @@ implicit none
procedure :: component_count => query_component_count
procedure :: get_value_by_index => get_query_value_from_index
procedure :: get_value_by_key => get_query_value_from_key
+ procedure :: has_key => query_has_key
generic, public :: get_value => get_value_by_index, get_value_by_key
@@ -240,4 +241,15 @@ contains
end function get_query_value_from_key
+ function query_has_key(self, key)
+ implicit none
+
+ class(query), intent(in)::self
+ character(*), intent(in)::key
+ logical::query_has_key
+
+ query_has_key = associated(self%get_value_by_key(key))
+
+ end function query_has_key
+
end module query_utilities
diff --git a/captain/response.f90 b/captain/response.f90
index e743d0d..b091a42 100644
--- a/captain/response.f90
+++ b/captain/response.f90
@@ -503,7 +503,7 @@ contains
if(j<=0) then
j = n
else
- j = j+i
+ j = j+i-1
end if
i = i + label_width + 2
diff --git a/captain/security.f90 b/captain/security.f90
new file mode 100644
index 0000000..2f5fa4c
--- /dev/null
+++ b/captain/security.f90
@@ -0,0 +1,107 @@
+! Copyright (c) 2021 Approximatrix, LLC <support@approximatrix.com>
+!
+! Permission is hereby granted, free of charge, to any person obtaining a copy
+! of this software and associated documentation files (the "Software"), to deal
+! in the Software without restriction, including without limitation the rights
+! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+! copies of the Software, and to permit persons to whom the Software is
+! furnished to do so, subject to the following conditions:
+!
+! The above copyright notice and this permission notice shall be included in
+! all copies or substantial portions of the Software.
+!
+! The Software shall be used for Good, not Evil.
+!
+! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+! SOFTWARE.
+
+module security
+implicit none
+
+contains
+
+ function validate_titan_token(token_string, player_name)
+ use captain_db
+ use logging
+ implicit none
+
+ character(*), intent(in)::token_string
+ character(*), intent(in), optional::player_name
+ logical::validate_titan_token
+ character(len=:), pointer::dbtoken, internal_player
+
+ integer::player_id, i, j
+
+ validate_titan_token = .false.
+
+ i = index(token_string, ":")
+ j = len_trim(token_string)
+
+ if(present(player_name)) then
+
+ allocate(character(len=len_trim(player_name)) :: internal_player)
+ internal_player = player_name
+
+ else
+
+ if(i > 0) then
+ allocate(character(len=i-1) :: internal_player)
+ internal_player = token_string(1:i-1)
+ end if
+
+ end if
+
+ if(associated(internal_player)) then
+
+ player_id = get_player_id(internal_player)
+ if(player_id >= 0) then
+
+ if(.not. player_has_token_db(internal_player)) then
+
+ validate_titan_token = .true.
+
+ else
+
+ allocate(character(len=(len_trim(token_string) + 1)) :: dbtoken)
+ dbtoken = " "
+
+ call get_player_token_db(internal_player, dbtoken)
+
+ if(i <= 0) then
+ i = 1
+ else
+ i = i + 1
+ end if
+
+ call write_log("Tokens '"//trim(dbtoken)//"' vs. '"//token_string(i:j)//"'")
+
+ validate_titan_token = (trim(dbtoken) == token_string(i:j))
+
+ deallocate(dbtoken)
+
+ end if
+
+ end if
+
+ if(validate_titan_token) then
+ call write_log("Titan token valid for "//trim(internal_player))
+ else
+ call write_log("Titan token FAILURE for "//trim(internal_player))
+ end if
+
+ deallocate(internal_player)
+
+ else
+
+ call write_log("Titan token did not include a player - REJECTED")
+
+ end if
+
+ end function validate_titan_token
+
+end module security \ No newline at end of file
diff --git a/common/protocol.f90 b/common/protocol.f90
index cf93b0d..25050cb 100644
--- a/common/protocol.f90
+++ b/common/protocol.f90
@@ -151,8 +151,8 @@ contains
response_line_index = 0
bytes_received = retrieve_characters(conn%ssl, buffer)
- do while(bytes_received > 0)
+ do while(bytes_received > 0)
do i=1, bytes_received
if(.not. response_line_completed) then
response_line_index = response_line_index + 1
@@ -286,14 +286,17 @@ contains
!Print *, "bytes read for sending: ", bytes_read
- do while(bytes_read > 0)
+ bytes_written = 0
+ do while(bytes_read > 0 .and. bytes_written >= 0)
bytes_written = ssl_write(conn%ssl, buffer(1:bytes_read))
total_written = total_written + bytes_written
bytes_read = read_into_buffer(unit_number, buffer)
!Print *, "bytes read for sending now: ", bytes_read, " and so far, we wrote", total_written
end do
- if(total_written >= file_length) then
+ if(total_written >= file_length .and. bytes_written >= 0) then
+
+ Print *, "Upload ok: ", total_written, file_length
response_line_completed = .false.
response_line = " "
@@ -321,6 +324,7 @@ contains
end do
else
+ Print *, "Marking localfail"
returncode = STATUS_LOCALFAIL
end if
diff --git a/player/instructions.f90 b/player/instructions.f90
index 4669f18..65b392d 100644
--- a/player/instructions.f90
+++ b/player/instructions.f90
@@ -381,7 +381,10 @@ contains
end if
if(get_task_failure_okay(j, i) .and. .not. success) then
+ Print *, "Reseting to failure"
success = .true.
+ else if(.not. success) then
+ Print *, "Task failure"
end if
end function perform_task
@@ -411,6 +414,7 @@ contains
do i = 1, task_count
call get_status_url(job_id, i, url, status=STATUS_STARTING)
+ Print *, "Reporting: "//trim(url)
server_status = request_to_ignored(url)
res = perform_task(j, i, captured_filename)
@@ -444,9 +448,11 @@ contains
else
call get_status_url(job_id, i, url, status=STATUS_FAILED)
server_status = request_to_ignored(url)
+ Print *, "Attempted reporting failure"
exit
endif
end if
+
end do
call get_job_report_url(job_id, res, url)
diff --git a/player/player.F90 b/player/player.F90
index 121b441..2233aff 100644
--- a/player/player.F90
+++ b/player/player.F90
@@ -71,6 +71,7 @@ implicit none
call get_instruction_name_from_checkin(j_checkin, instruction_name)
call get_instruction_url(instruction_name, url)
+
instr_json_available = request_json(url, j_instructions)
if(instr_json_available) then
@@ -117,7 +118,8 @@ contains
Print *, " -h Display this help"
Print *, " -w <dir> Use dir as the working directory"
Print *, " -l <log> Use log as the logfile"
- Print *, " -i <identity> This players identity"
+ Print *, " -i <identity> This player's identity"
+ Print *, " -t <token> This player's security token"
end subroutine usage
@@ -158,6 +160,10 @@ contains
else if(trim(option) == "-i") then
i = i + 1
call get_command_argument(i, identity)
+
+ else if(trim(option) == "-t") then
+ i = i + 1
+ call get_command_argument(i, token)
end if
diff --git a/player/talking.f90 b/player/talking.f90
index 42b6451..2171f41 100644
--- a/player/talking.f90
+++ b/player/talking.f90
@@ -94,6 +94,7 @@ contains
open(newunit=io, status="scratch", access='stream')
status_code = request_url(mod_url, io, return_type)
+ Print *, status_code
close(io)
deallocate(mod_url)
@@ -102,7 +103,7 @@ contains
function send_file(url, filename) result(status_code)
use gemini_protocol
- use config, only: token
+ use config, only: token, identity
implicit none
character(*), intent(in)::url
@@ -125,7 +126,7 @@ contains
access='STREAM', form='UNFORMATTED', iostat=istatus)
if(istatus == 0) then
- status_code = titan_post_url(mod_url, unit_number, file_size, token)
+ status_code = titan_post_url(mod_url, unit_number, file_size, trim(identity)//":"//trim(token))
close(unit_number)
else
status_code = STATUS_LOCALFAIL
diff --git a/player/tasks.f90 b/player/tasks.f90
index e0eecfd..b0f2cc1 100644
--- a/player/tasks.f90
+++ b/player/tasks.f90
@@ -118,7 +118,7 @@ contains
allocate(character(len=len_trim(mask)+DIR_LIST_STRING_LENGTH+1) :: fullname)
call path_from_file(mask, dir)
- allocate(statuses(size(files)))
+ allocate(statuses(size(files)))
do i = 1, size(files)
! On some systems, we're getting subdirectories back...
@@ -143,11 +143,15 @@ contains
res = .false.
end if
+
+ if(.not. res) then
+ Print *, "Glob failure"
+ end if
end function upload_glob
function upload(url, source_filename) result(res)
- use config, only: token, captain
+ use config, only: token, captain, identity
use gemini_protocol, only: titan_post_url, STATUS_SUCCESS
implicit none
@@ -205,7 +209,8 @@ contains
if(istatus == 0) then
Print *, "Writing "//trim(mod_url)
- istatus = titan_post_url(mod_url, unit_number, file_size, token)
+ istatus = titan_post_url(mod_url, unit_number, file_size, trim(identity)//":"//trim(token))
+ Print *, "Response code from server: ", istatus
res = (istatus == STATUS_SUCCESS)
close(unit_number)
else
@@ -214,6 +219,10 @@ contains
deallocate(mod_url)
+ if(.not. res) then
+ Print *, "Upload Failure"
+ end if
+
end function upload
function download(url, destination_filename)