diff --git a/htroot/ConfigAccounts_p.java b/htroot/ConfigAccounts_p.java index 5c8fc4e7f..1df7ca608 100644 --- a/htroot/ConfigAccounts_p.java +++ b/htroot/ConfigAccounts_p.java @@ -74,11 +74,11 @@ public class ConfigAccounts_p { final String pw2 = post.get("adminpw2", ""); int inputerror=0; // may be overwritten if new password is given - if (user.length() > 0 && pw1.length() > 2 && pw1.equals(pw2)) { + if (user.length() > 0 && pw1.equals(pw2)) { String oldusername = env.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME,user); // check passed. set account: // old: // env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, Digest.encodeMD5Hex(Base64Order.standardCoder.encodeString(user + ":" + pw1))); - env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "MD5:"+Digest.encodeMD5Hex(user + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy")+":"+ pw1)); + env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, sb.encodeDigestAuth(user, pw1)); env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME,user); // make sure server accepts new credentials Jetty9HttpServerImpl jhttpserver = (Jetty9HttpServerImpl)sb.getHttpServer(); @@ -210,7 +210,7 @@ public class ConfigAccounts_p { if (!"".equals(pw1)) { //change only if set // MD5 according to HTTP Digest RFC 2617 (3.2.2) name:realm:pwd (use seed.hash as realm) // with prefix of encoding method (supported MD5: ) - mem.put(UserDB.Entry.MD5ENCODED_USERPWD_STRING, "MD5:"+Digest.encodeMD5Hex(username + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy")+":"+pw1)); + mem.put(UserDB.Entry.MD5ENCODED_USERPWD_STRING, sb.encodeDigestAuth(username, pw1)); } mem.put(UserDB.Entry.USER_FIRSTNAME, firstName); @@ -238,11 +238,8 @@ public class ConfigAccounts_p { if (entry != null) { try{ - if (!"".equals(pw1)) { - // with prefix of encoding method (supported MD5: ) - entry.setProperty(UserDB.Entry.MD5ENCODED_USERPWD_STRING, "MD5:"+Digest.encodeMD5Hex(username+ ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy") + ":"+pw1)); - } - + // with prefix of encoding method (supported MD5: ) + entry.setProperty(UserDB.Entry.MD5ENCODED_USERPWD_STRING, sb.encodeDigestAuth(username, pw1)); entry.setProperty(UserDB.Entry.USER_FIRSTNAME, firstName); entry.setProperty(UserDB.Entry.USER_LASTNAME, lastName); entry.setProperty(UserDB.Entry.USER_ADDRESS, address); diff --git a/htroot/ConfigUser_p.java b/htroot/ConfigUser_p.java index 134b1bf16..6818dc5e9 100644 --- a/htroot/ConfigUser_p.java +++ b/htroot/ConfigUser_p.java @@ -121,11 +121,8 @@ public class ConfigUser_p { if (entry != null) { try { - if (!"".equals(pw1)) { - // with prefix of encoding method (supported MD5: ) - entry.setProperty(UserDB.Entry.MD5ENCODED_USERPWD_STRING, "MD5:" + Digest.encodeMD5Hex(username + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM, "YaCy") + ":" + pw1)); - } - + // with prefix of encoding method (supported MD5: ) + entry.setProperty(UserDB.Entry.MD5ENCODED_USERPWD_STRING, sb.encodeDigestAuth(username, pw1)); entry.setProperty(UserDB.Entry.USER_FIRSTNAME, firstName); entry.setProperty(UserDB.Entry.USER_LASTNAME, lastName); entry.setProperty(UserDB.Entry.USER_ADDRESS, address); diff --git a/htroot/SettingsAck_p.java b/htroot/SettingsAck_p.java index 745367801..13f16fafd 100644 --- a/htroot/SettingsAck_p.java +++ b/htroot/SettingsAck_p.java @@ -91,7 +91,7 @@ public class SettingsAck_p { return prop; } // check passed. set account: - env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "MD5:"+Digest.encodeMD5Hex(user + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy") + ":" + pw1)); + env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, sb.encodeDigestAuth(user, pw1)); env.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME, user); prop.put("info", "5");//admin account changed prop.putHTML("info_user", user); diff --git a/htroot/User.java b/htroot/User.java index f38bb276d..5f6fb9daa 100644 --- a/htroot/User.java +++ b/htroot/User.java @@ -134,12 +134,12 @@ public class User{ prop.put("status", "1"); //password if (entry.getMD5EncodedUserPwd().startsWith("MD5:") ? - entry.getMD5EncodedUserPwd().equals("MD5:"+Digest.encodeMD5Hex(entry.getUserName() + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy") + ":" + post.get("oldpass", ""))) : - entry.getMD5EncodedUserPwd().equals(Digest.encodeMD5Hex(entry.getUserName() + ":" + post.get("oldpass", "")))) { + entry.getMD5EncodedUserPwd().equals(sb.encodeDigestAuth(entry.getUserName(), post.get("oldpass", ""))) : + entry.getMD5EncodedUserPwd().equals(sb.encodeBasicAuth(entry.getUserName() , post.get("oldpass", "")))) { if (post.get("newpass").equals(post.get("newpass2"))) { if (!post.get("newpass", "").equals("")) { try { - entry.setProperty(UserDB.Entry.MD5ENCODED_USERPWD_STRING, "MD5:" + Digest.encodeMD5Hex(entry.getUserName() + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy") + ":" + post.get("newpass", ""))); + entry.setProperty(UserDB.Entry.MD5ENCODED_USERPWD_STRING, sb.encodeDigestAuth(entry.getUserName(), post.get("newpass", ""))); prop.put("status_password", "0"); //changes } catch (final Exception e) { ConcurrentLog.logException(e); diff --git a/source/net/yacy/data/TransactionManager.java b/source/net/yacy/data/TransactionManager.java index 2a5487ba5..6b5f21c18 100644 --- a/source/net/yacy/data/TransactionManager.java +++ b/source/net/yacy/data/TransactionManager.java @@ -63,28 +63,36 @@ public class TransactionManager { */ private static String getUserName(final RequestHeader header) { String userName = header.getRemoteUser(); - - if (userName == null && header.accessFromLocalhost() && Switchboard.getSwitchboard() != null) { - final String adminAccountUserName = Switchboard.getSwitchboard().getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME, "admin"); - final String adminAccountBase64MD5 = Switchboard.getSwitchboard().getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, ""); - - if(Switchboard.getSwitchboard() - .getConfigBool(SwitchboardConstants.ADMIN_ACCOUNT_FOR_LOCALHOST, false)) { - /* Unauthenticated local access as administrator can be enabled */ - userName = adminAccountUserName; - } else { - /* authorization by encoded password, only for localhost access (used by bash scripts)*/ - String pass = Base64Order.standardCoder.encodeString(adminAccountUserName + ":" + adminAccountBase64MD5); - - /* get the authorization string from the header */ - final String realmProp = (header.get(RequestHeader.AUTHORIZATION, "")).trim(); - final String realmValue = realmProp.isEmpty() ? null : realmProp.substring(6); // take out "BASIC " - - if (pass.equals(realmValue)) { // assume realmValue as is in cfg - userName = adminAccountUserName; - } - } - } + Switchboard sb = Switchboard.getSwitchboard(); + + if (sb != null) { + final String adminAccountBase64MD5 = sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, ""); + final String adminAccountUserName = sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME, "admin"); + if (adminAccountBase64MD5.equals(sb.emptyPasswordAdminAccount)) { + // admin users with empty passwords do not need to authentify, thus do not have + // this header present. We just consoder the name is "admin" + userName = adminAccountUserName; + } + + if (userName == null && header.accessFromLocalhost()) { + + if (sb.getConfigBool(SwitchboardConstants.ADMIN_ACCOUNT_FOR_LOCALHOST, false)) { + /* Unauthenticated local access as administrator can be enabled */ + userName = adminAccountUserName; + } else { + /* authorization by encoded password, only for localhost access (used by bash scripts)*/ + String pass = Base64Order.standardCoder.encodeString(adminAccountUserName + ":" + adminAccountBase64MD5); + + /* get the authorization string from the header */ + final String realmProp = (header.get(RequestHeader.AUTHORIZATION, "")).trim(); + final String realmValue = realmProp.isEmpty() ? null : realmProp.substring(6); // take out "BASIC " + + if (pass.equals(realmValue)) { // assume realmValue as is in cfg + userName = adminAccountUserName; + } + } + } + } return userName; } diff --git a/source/net/yacy/http/Jetty9YaCySecurityHandler.java b/source/net/yacy/http/Jetty9YaCySecurityHandler.java index cb58ef7ff..3196a618d 100644 --- a/source/net/yacy/http/Jetty9YaCySecurityHandler.java +++ b/source/net/yacy/http/Jetty9YaCySecurityHandler.java @@ -76,14 +76,18 @@ public class Jetty9YaCySecurityHandler extends ConstraintSecurityHandler { // ! note : accessFromLocalhost compares localhost ip pattern final boolean grantedForLocalhost = adminAccountGrantedForLocalhost && accessFromLocalhost; - /* Even when all pages are protected, we don't want to block those used for peer-to-peer or cluster communication (except in private robinson mode) - * (examples : /yacy/hello.html is required for p2p and cluster network presence and /solr/select for remote Solr search requests) */ + // Even when all pages are protected, we don't want to block those used for peer-to-peer or cluster communication (except in private robinson mode) + // (examples : /yacy/hello.html is required for p2p and cluster network presence and /solr/select for remote Solr search requests) boolean protectedPage = (adminAccountNeededForAllPages && ((sb.isRobinsonMode() && !sb.isPublicRobinson()) || !(pathInContext.startsWith("/yacy/") || pathInContext.startsWith("/solr/")))); - /* Pages suffixed with "_p" are by the way always considered protected */ + // Pages suffixed with "_p" are by the way always considered protected protectedPage = protectedPage || (pathInContext.indexOf("_p.") > 0); + // ..except that the password for the admin account is empty + final String adminAccountBase64MD5 = sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, ""); + protectedPage = protectedPage && !adminAccountBase64MD5.equals(sb.emptyPasswordAdminAccount); + // check "/gsa" and "/solr" if not publicSearchpage if (!protectedPage && !sb.getConfigBool(SwitchboardConstants.PUBLIC_SEARCHPAGE, true)) { protectedPage = pathInContext.startsWith("/solr/") || pathInContext.startsWith("/gsa/"); @@ -97,7 +101,6 @@ public class Jetty9YaCySecurityHandler extends ConstraintSecurityHandler { final String credentials = request.getHeader(RequestHeader.AUTHORIZATION); if (credentials != null && credentials.length() < 120 && credentials.startsWith("Basic ")) { // Basic credentials are short "Basic " + b64(user:pwd) final String foruser = sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME, "admin"); - final String adminAccountBase64MD5 = sb.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, ""); final String b64 = Base64Order.standardCoder.encodeString(foruser + ":" + adminAccountBase64MD5); // TODO: is this valid? ; consider "MD5:" prefixed config if ((credentials.substring(6)).equals(b64)) return null; // lazy authentication for local access with credential from config (only a user with read access to DATA can do that) } diff --git a/source/net/yacy/search/Switchboard.java b/source/net/yacy/search/Switchboard.java index 352548bfe..4f61ce188 100644 --- a/source/net/yacy/search/Switchboard.java +++ b/source/net/yacy/search/Switchboard.java @@ -324,6 +324,7 @@ public final class Switchboard extends serverSwitch { private boolean startupAction = true; // this is set to false after the first event private static Switchboard sb; public HashMap crawlJobsStatus = new HashMap(); + public String emptyPasswordAdminAccount; public Switchboard(final File dataPath, final File appPath, final String initPath, final String configPath) { super(dataPath, appPath, initPath, configPath); @@ -456,6 +457,9 @@ public final class Switchboard extends serverSwitch { } }.start(); + // define the "non-password password" + emptyPasswordAdminAccount = encodeDigestAuth(getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME,"admin"), ""); + // init the language detector this.log.config("Loading language profiles"); try { @@ -3980,6 +3984,8 @@ public final class Switchboard extends serverSwitch { * - access from localhost is granted and access comes from localhost: auth-level 3 * - a password is configured and access comes from localhost and the realm-value * of a http-authentify String is equal to the stored base64MD5: auth-level 3 + * - an empty password is configured an access comes from anywhere: auth-level 3 + * This may be used in cluster installations where the cluster has an outside protection but inside is none needed. * - a password is configured and access comes with matching http-authentify: auth-level 4 * * @param requestHeader @@ -4006,6 +4012,11 @@ public final class Switchboard extends serverSwitch { return 2; // no password stored; this should not happen for older peers } + // authorization in case that administrators have stored an empty password; this authorizes all users as admin regardless of the give auth + if (adminAccountBase64MD5.equals(emptyPasswordAdminAccount)) { + return 3; // everyone is admin from everywhere + } + // authorization for localhost, only if flag is set to grant localhost access as admin final boolean accessFromLocalhost = requestHeader.accessFromLocalhost(); if (accessFromLocalhost && getConfigBool(SwitchboardConstants.ADMIN_ACCOUNT_FOR_LOCALHOST, false)) { @@ -4083,6 +4094,14 @@ public final class Switchboard extends serverSwitch { return 1; } + public String encodeDigestAuth(String user, String pw) { + return "MD5:" + Digest.encodeMD5Hex(user + ":" + sb.getConfig(SwitchboardConstants.ADMIN_REALM,"YaCy") + ":" + pw); + } + + public String encodeBasicAuth(String user, String pw) { + return Digest.encodeMD5Hex(user + ":" + pw); + } + /** * @param header servlet request headers * @return true when the headers contains valid admin authentication information diff --git a/source/net/yacy/yacy.java b/source/net/yacy/yacy.java index d114fef92..6b9a5e0d2 100644 --- a/source/net/yacy/yacy.java +++ b/source/net/yacy/yacy.java @@ -791,7 +791,7 @@ public final class yacy { } else { username = ss.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_USER_NAME, "admin"); } - ss.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "MD5:" + Digest.encodeMD5Hex(username + ":" + ss.getConfig(SwitchboardConstants.ADMIN_REALM, "YaCy") + ":" + pwdtxt)); + ss.setConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, sb.encodeDigestAuth(username, pwdtxt)); System.out.println("Set property " + SwitchboardConstants.ADMIN_ACCOUNT_B64MD5 + " = " + ss.getConfig(SwitchboardConstants.ADMIN_ACCOUNT_B64MD5, "")); } } else {