Index: openacs-4/packages/acs-tcl/acs-tcl.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/acs-tcl.info,v diff -u -r1.95.2.49 -r1.95.2.50 --- openacs-4/packages/acs-tcl/acs-tcl.info 11 Aug 2022 13:49:46 -0000 1.95.2.49 +++ openacs-4/packages/acs-tcl/acs-tcl.info 25 Aug 2022 11:25:10 -0000 1.95.2.50 @@ -9,7 +9,7 @@ f t - + OpenACS The Kernel Tcl API library. 2021-09-15 @@ -18,7 +18,7 @@ GPL version 2 3 - + Index: openacs-4/packages/acs-tcl/tcl/security-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/security-procs.tcl,v diff -u -r1.126.2.70 -r1.126.2.71 --- openacs-4/packages/acs-tcl/tcl/security-procs.tcl 24 Aug 2022 14:51:25 -0000 1.126.2.70 +++ openacs-4/packages/acs-tcl/tcl/security-procs.tcl 25 Aug 2022 11:25:10 -0000 1.126.2.71 @@ -1172,7 +1172,60 @@ host_node_id $host_node_id] } +ad_proc security::safe_tmpfile_p {tmpfile} { + Checks that a file is a safe tmpfile, that is, it belongs to the + configured tmpdir. + + When the file exists, we also enforce additional criteria: + - file must belong to the current system user + - file must be readable and writeable by the current system user + + @param tmpfile absolute path to a possibly existing tmpfile + + @return boolean +} { + if {[ad_file dir $tmpfile] ne [ns_config ns/parameters tmpdir]} { + # + # File does not belong to the tmpdir: not safe + # + return false + } + + if {![ad_file exists $tmpfile]} { + # + # File does not exist yet: safe + # + return true + } + + if {![ad_file owned $tmpfile]} { + # + # File does not belong to us: not safe + # + return false + } + + if {![ad_file readable $tmpfile]} { + # + # We cannot read the file: not safe + # + return false + } + + if {![ad_file writable $tmpfile]} { + # + # We cannot write the file: not safe + # + return false + } + + # + # The file is safe + # + return true +} + ad_proc -public ad_get_login_url { {-authority_id ""} {-username ""} Index: openacs-4/packages/acs-tcl/tcl/test/security-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/test/security-procs.tcl,v diff -u -r1.1.2.4 -r1.1.2.5 --- openacs-4/packages/acs-tcl/tcl/test/security-procs.tcl 2 Aug 2021 17:36:36 -0000 1.1.2.4 +++ openacs-4/packages/acs-tcl/tcl/test/security-procs.tcl 25 Aug 2022 11:25:11 -0000 1.1.2.5 @@ -90,3 +90,48 @@ ns_unregister_op GET $test_url } } + +aa_register_case \ + -cats { api } \ + -procs { + security::safe_tmpfile_p + ad_tmpnam + acs_root_dir + ad_file + } \ + safe_tmpfile_p { + + Test security::safe_tmpfile_p proc + + } { + set tmpfile [ad_tmpnam] + aa_section {Path to a tmpfile that does not exist yet} + aa_true "A temporary filename is safe" [security::safe_tmpfile_p $tmpfile] + + aa_section {Path to an existing tmpfile} + set wfd [open $tmpfile w] + puts $wfd 1234 + close $wfd + aa_true "An existing tmpfile is safe" [security::safe_tmpfile_p $tmpfile] + file delete -- $tmpfile + + aa_section {Path to a tmpfile in a folder of the tmpdir} + set tmpfile [ad_tmpnam]/test + aa_false "A safe tmpfile can only be a direct child of the tmpdir" \ + [security::safe_tmpfile_p $tmpfile] + + aa_section {Trying to confuse the proc with ".."} + set tmpfile [ad_tmpnam]/../test + aa_false "Proc is not fooled by .." \ + [security::safe_tmpfile_p $tmpfile] + + aa_section {Trying to confuse the proc with "~"} + set tmpfile ~/../../test + aa_false "Proc is not fooled by ~" \ + [security::safe_tmpfile_p $tmpfile] + + aa_section {Path to a file outside of the tmpdir} + set tmpfile [acs_root_dir]/mypreciouscode + aa_false "A safe tmpfile can only be a direct child of the tmpdir" \ + [security::safe_tmpfile_p $tmpfile] + }