*) Made sure that only files with appropriate file endings are listed as skin or language files.

*) Introduced protection against directory traversal attacks in configuration servlets for skin and language configuration. Files can only be deleted if they are contained in a list of files which has been read by the servlet first.


Until now it was possible to delete any data on a system YaCy is running on and which can be deleted by the user who's account has been used to start YaCy. Most of the times a user of YaCy is also the owner of the machine the peer is running on, but this might not always be the case and not even the owner of the machine should be able to use YaCy as a replacement for "rm" or "del".

git-svn-id: https://svn.berlios.de/svnroot/repos/yacy/trunk@6423 6c8d7289-2bf4-0310-a012-ef5d649a1542
pull/1/head
low012 16 years ago
parent 3434ca381f
commit f5656b2ae1

@ -166,9 +166,9 @@ public class Blacklist_p {
return prop; return prop;
} }
final File BlackListFile = new File(listManager.listsPath, blacklistToUse); final File blackListFile = new File(listManager.listsPath, blacklistToUse);
if(!BlackListFile.delete()) { if(!blackListFile.delete()) {
Log.logWarning("Blacklist", "file "+ BlackListFile +" could not be deleted!"); Log.logWarning("Blacklist", "file "+ blackListFile +" could not be deleted!");
} }
for (int blTypes=0; blTypes < supportedBlacklistTypes.length; blTypes++) { for (int blTypes=0; blTypes < supportedBlacklistTypes.length; blTypes++) {

@ -48,6 +48,8 @@
<dd><input type="checkbox" name="use_skin" id="use_url" value="on" checked="checked" /></dd> <dd><input type="checkbox" name="use_skin" id="use_url" value="on" checked="checked" /></dd>
<dd><input type="submit" name="install_button" value="Install" /></dd> <dd><input type="submit" name="install_button" value="Install" /></dd>
</dl> </dl>
<p>Make sure that you only download data from trustworthy sources. The new language file
might overwrite existing data if a file of the same name exists already.</p>
</fieldset> </fieldset>
</form> </form>

@ -52,7 +52,9 @@ import java.util.Collections;
public class ConfigAppearance_p { public class ConfigAppearance_p {
public static serverObjects respond(final RequestHeader header, final serverObjects post, final serverSwitch env) { private final static String SKIN_FILENAME_FILTER = "^.*\\.css$";
public static serverObjects respond(final RequestHeader header, final serverObjects post, final serverSwitch env) {
final serverObjects prop = new serverObjects(); final serverObjects prop = new serverObjects();
final Switchboard sb = (Switchboard) env; final Switchboard sb = (Switchboard) env;
final String skinPath = new File(env.getRootPath(), env.getConfig("skinPath", "DATA/SKINS")).toString(); final String skinPath = new File(env.getRootPath(), env.getConfig("skinPath", "DATA/SKINS")).toString();
@ -61,26 +63,42 @@ public class ConfigAppearance_p {
prop.put("currentskin", ""); prop.put("currentskin", "");
prop.put("status", "0"); // nothing prop.put("status", "0"); // nothing
List<String> skinFiles = listManager.getDirListing(skinPath); List<String> skinFiles = listManager.getDirListing(skinPath, SKIN_FILENAME_FILTER);
if (skinFiles == null) { if (skinFiles == null) {
return prop; return prop;
} }
if (post != null) { if (post != null) {
if (post.containsKey("use_button") && post.get("skin") != null) { String selectedSkin = post.get("skin");
// change skin
changeSkin(sb, skinPath, post.get("skin"));
if (post.containsKey("use_button") && selectedSkin != null) {
/* Only change skin if filename is contained in list of filesnames
* read from the skin directory. This is very important to prevent
* directory traversal attacks!
*/
if (skinFiles.contains(selectedSkin)) {
changeSkin(sb, skinPath, selectedSkin);
}
} }
if (post.containsKey("delete_button")) { if (post.containsKey("delete_button")) {
// delete skin
final File skinfile = new File(skinPath, post.get("skin")); /* Only delete file if filename is contained in list of filesname
FileUtils.deletedelete(skinfile); * read from the skin directory. This is very important to prevent
* directory traversal attacks!
*/
if (skinFiles.contains(selectedSkin)) {
final File skinfile = new File(skinPath, selectedSkin);
FileUtils.deletedelete(skinfile);
}
} }
if (post.containsKey("install_button")) { if (post.containsKey("install_button")) {
// load skin from URL // load skin from URL
final String url = post.get("url"); final String url = post.get("url");
final File skinFile = new File(skinPath, url.substring(url.lastIndexOf("/"), url.length()));
Iterator<String> it; Iterator<String> it;
try { try {
final DigestURI u = new DigestURI(url, null); final DigestURI u = new DigestURI(url, null);
@ -93,7 +111,6 @@ public class ConfigAppearance_p {
return prop; return prop;
} }
try { try {
final File skinFile = new File(skinPath, url.substring(url.lastIndexOf("/"), url.length()));
final BufferedWriter bw = new BufferedWriter(new PrintWriter(new FileWriter(skinFile))); final BufferedWriter bw = new BufferedWriter(new PrintWriter(new FileWriter(skinFile)));
while (it.hasNext()) { while (it.hasNext()) {
@ -112,7 +129,7 @@ public class ConfigAppearance_p {
} }
// reread skins // reread skins
skinFiles = listManager.getDirListing(skinPath); skinFiles = listManager.getDirListing(skinPath, SKIN_FILENAME_FILTER);
Collections.sort(skinFiles); Collections.sort(skinFiles);
int count = 0; int count = 0;
for (String skinFile : skinFiles) { for (String skinFile : skinFiles) {

@ -51,6 +51,8 @@
</dd> </dd>
<dd><input type="submit" value="Install" /></dd> <dd><input type="submit" value="Install" /></dd>
</dl> </dl>
<p>Make sure that you only download data from trustworthy sources. The new language file
might overwrite existing data if a file of the same name exists already.</p>
</fieldset> </fieldset>
</form> </form>
#(status)# #(status)#

@ -54,8 +54,10 @@ import java.util.Collections;
public class ConfigLanguage_p { public class ConfigLanguage_p {
private final static String LANG_FILENAME_FILTER = "^.*\\.lng$";
public static serverObjects respond(final RequestHeader header, final serverObjects post, final serverSwitch env) { public static serverObjects respond(final RequestHeader header, final serverObjects post, final serverSwitch env) {
//listManager.switchboard = (plasmaSwitchboard) env;
final serverObjects prop = new serverObjects(); final serverObjects prop = new serverObjects();
final String langPath = env.getConfigPath("locale.work", "DATA/LOCALE/locales").getAbsolutePath(); final String langPath = env.getConfigPath("locale.work", "DATA/LOCALE/locales").getAbsolutePath();
@ -63,20 +65,35 @@ public class ConfigLanguage_p {
//prop.put("currentlang", ""); //is done by Translationtemplate //prop.put("currentlang", ""); //is done by Translationtemplate
prop.put("status", "0");//nothing prop.put("status", "0");//nothing
List<String> langFiles = listManager.getDirListing(langPath); List<String> langFiles = listManager.getDirListing(langPath, LANG_FILENAME_FILTER);
if(langFiles == null){ if(langFiles == null){
return prop; return prop;
} }
if (post != null){ if (post != null){
String selectedLanguage = post.get("language");
//change language //change language
if(post.containsKey("use_button") && post.get("language") != null){ if(post.containsKey("use_button") && selectedLanguage != null){
translator.changeLang(env, langPath, post.get("language")); /* Only change language if filename is contained in list of filesnames
* read from the language directory. This is very important to prevent
* directory traversal attacks!
*/
if (langFiles.contains(selectedLanguage)) {
translator.changeLang(env, langPath, selectedLanguage);
}
//delete language file //delete language file
}else if(post.containsKey("delete")){ }else if(post.containsKey("delete")){
final File langfile= new File(langPath, post.get("language"));
FileUtils.deletedelete(langfile); /* Only delete file if filename is contained in list of filesnames
* read from the language directory. This is very important to prevent
* directory traversal attacks!
*/
if (langFiles.contains(selectedLanguage)) {
final File langfile= new File(langPath, selectedLanguage);
FileUtils.deletedelete(langfile);
}
//load language file from URL //load language file from URL
} else if (post.containsKey("url")){ } else if (post.containsKey("url")){
@ -111,7 +128,7 @@ public class ConfigLanguage_p {
} }
//reread language files //reread language files
langFiles = listManager.getDirListing(langPath); langFiles = listManager.getDirListing(langPath, LANG_FILENAME_FILTER);
Collections.sort(langFiles); Collections.sort(langFiles);
final HashMap<String, String> langNames = translator.langMap(env); final HashMap<String, String> langNames = translator.langMap(env);
String langKey, langName; String langKey, langName;
@ -123,20 +140,18 @@ public class ConfigLanguage_p {
int count = 0; int count = 0;
for(String langFile : langFiles){ for(String langFile : langFiles){
if(langFile.endsWith(".lng")){ //+1 because of the virtual entry "default" at top
//+1 because of the virtual entry "default" at top langKey = langFile.substring(0, langFile.length() -4);
langKey = langFile.substring(0, langFile.length() -4); langName = langNames.get(langKey);
langName = langNames.get(langKey); prop.put("langlist_" + (count + 1) + "_file", langFile);
prop.put("langlist_" + (count + 1) + "_file", langFile); prop.put("langlist_" + (count + 1) + "_name", ((langName == null) ? langKey : langName));
prop.put("langlist_" + (count + 1) + "_name", ((langName == null) ? langKey : langName)); if(env.getConfig("locale.language", "default").equals(langKey)) {
if(env.getConfig("locale.language", "default").equals(langKey)) { prop.put("langlist_" + (count + 1) + "_selected", "selected=\"selected\"");
prop.put("langlist_" + (count + 1) + "_selected", "selected=\"selected\""); prop.put("langlist_0_selected", " "); // reset Default
prop.put("langlist_0_selected", " "); // reset Default } else {
} else { prop.put("langlist_" + (count + 1) + "_selected", " ");
prop.put("langlist_" + (count + 1) + "_selected", " ");
}
count++;
} }
count++;
} }
prop.put("langlist", (count + 1)); prop.put("langlist", (count + 1));

Loading…
Cancel
Save