% proc private varname { set varname [uplevel 1 [list namespace which -variable $varname]] upvar 1 $varname v trace add variable v {read write unset} [list CheckPerms $varname] } % proc CheckPerms {varname args} { set n [namespace qualifiers $varname] set c [string trimright [uplevel 1 [list namespace current]] "::"] if {$n ne $c} { return -code error "access violation: $c cannot access $varname" } } % namespace eval foo { variable name "Neil" private name variable age 24 private age } % set foo::name can't read "foo::name": access violation: cannot access ::foo::name % namespace eval foo { set name } NeilThe presence of namespace eval allows it to be defeated, though. You could perhaps do something more clever with checking [info level -1] to see what namespace the calling proc is in, but I don't know if you could make it absolutely secure. If you just want some protection against coding mistakes, then something like the above should do. If you really want security, then I'd go with an interp. A lightweight "better namespace" which could handle all these sorts of situations would be cool, though.CMcC I wrote a system where untrusted code could be run from the net, in safe interps, of course. However it was quite expensive (each of the fragments of code had to persist) in that it required an interpreter per fragment. A single safe interp would suffice to protect the system from malicious code, but not to protect one piece of code from another. I think that private namespaces would go some way to achieving this goal more cheaply than safe interp.Lars H: Doesn't it rather achieve the opposite of that goal? This sounds mostly like the kind of thing a secret-loving company would ask for: it makes it possible for them to sell you a box and promise that it works miracles, but forbid you to look into the box. The private namespace keeps others from looking at the "magic", but it does nothing to protect the global environment, which security-wise is much more of an issue. A malicious script would typically only have to replace ::set with its own trojan to get a foothold into an arbitrary private environment.CMcC: No, it doesn't rather achieve the opposite of that goal. If it's not possible to form the name of an object in the private namespace, then it's not possible to access it by name. Seems straight-forward. Your comments, Lars, about how my suggestion 'sounds to you' sound insulting to me.PWQ CMcC:, I would have to agree with Lars H, there will always be some way to circumvent the access , unless you redo the internals of TCL. If creating interps is expensive then you need to look at either reusing an interp, or look at a TIP to speed up the process. How about a interp clone so that you can take a template interp and quickly make a copy?Lars H: Indeed. My point (which it seems I will have to make very explicit if it is to get through to the proposer) is that even code inside an invisible namespace will make use of the standard commands in the language, and these reside not far out of reach but in the very public :: namespace. Any command that a proc in a "private" namespace makes use of (e.g. set to set one of its local variables) will be perfectly able to access anything in that namespace using uplevel or the like, so a trojan imitating set could easily create its trapdoor command from within that private namespace. All it would take is something like
proc trojan_set {args} { if {![llength [uplevel 1 {info commands trapdoor}]]} then { uplevel 1 {::proc trapdoor script {::eval $script}; ::namespace export trapdoor} } uplevel 1 [linsert $args 0 ::original_set] } rename set original_set; rename trojan_set setPrivate namespaces would not give security but only obscurity (which admittedly though is a commonly employed approach for creating the illusion of security).
CMcC: Lars H's assumption "that even code inside an invisible namespace will make use of the standard commands in the language" (my emphasis) seems trivially false (if by will we take him to mean must): One can construct any number of scripts which do not make use of any standard commands in the language.Thank you, Lars, for pointing out that the implementation of private namespaces must not be able to access :: for command resolution, and that there are a number of commands which would have to be hidden from such a namespace.RHS Outside of C-level coding, can you come up with some example code that doesn't use and standard commands in the language? I'm drawing a blank.CMcC Redefine the whole set of standard commands. Run any script in that context, QED. Code can't and doesn't know if it's running original commands or not.Lars H: The meaning of "standard commands" that is relevant here is of course "something that has the same name (and syntax) as a standard command", so your argument is pointless.(I don't think 'pointless' means what you think it means - CMcC)Lars H ... It does not matter for the trojan whether it is replacing the real set or some modified form thereof, it only matters that the script it is invading calls a command named set. You may hope to redefine core commands so that the creation of a trojan becomes impossible, but that is a program with low chance of success; run-time code analysis is very hard.(I don't believe I've seen your argument that runtime code analysis is necessary for providing a safe subset of core commands with useful functionality. Pending that argument, I'll demur on your assessment of probabilities - CMcC)Lars H ... In summary: The idea of private namespaces does not provide any security with less than very extensive redefinitions of all standard commands, and each sandbox thus created will have to provide separate copies (at least as aliases or the like) of these redefined standard commands to keep them clean from interference. This amount of duplication and redirection makes it likely that the associated cost exceeds that of separate interpreters.CMcC except that the redefined commands can be shared between each private namespace. It's likely a space/time tradeoff with more cost in time and less in space.RHS I'll repeat my question. Can you, CMcC, show example code that does anything without calling any core Tcl commands?(I believe I adequately answered your question the first time. - CMcC)RHS ... At the very least, if you want to redefine other core commands, you need to call proc. At that point, you've already gone somewhere with your code that you don't have control over. Hence, I don't see how its possible to totally isolate namespace code. (Assuming, of course, that some other measure wasn't added... ie, the core placing all core commands in ::tcl, and disallowing redefinition of those commands)CMcC implementation detail. Prevent code from a private namespace calling anything namespace-path-explicitly, prevent it from changing its imports, provide safe commands. You're done. The argument from proc is inefficacious - proc doesn't give you any more or less access to scopes than evaluation in namespace scope.