From 1f64223e5cc3a25745f6910518b43e81690b7041 Mon Sep 17 00:00:00 2001 From: Rich Megginson Date: Thu, 10 Oct 2013 15:49:34 -0600 Subject: [PATCH] Ticket #222 Admin Express issues "Internal Server Error" when the Config DS is down. https://fedorahosted.org/389/ticket/222 Reviewed by: ??? Branch: master Fix Description: When the config ds is down, show a page to the user with a button to start the config ds. If the user presses the button, the config ds is started, and the user is redirected back to the admin server home page. Platforms tested: RHEL6 x86_64 Flag Day: no Doc impact: no --- admserv/cgi-src40/start_config_ds.c | 11 +++ mod_admserv/mod_admserv.c | 150 ++++++++++++++++++++++++----------- 2 files changed, 114 insertions(+), 47 deletions(-) diff --git a/admserv/cgi-src40/start_config_ds.c b/admserv/cgi-src40/start_config_ds.c index 299db1c..de29887 100644 --- a/admserv/cgi-src40/start_config_ds.c +++ b/admserv/cgi-src40/start_config_ds.c @@ -62,11 +62,16 @@ success_exit() int main(int argc, char *argv[]) { + int _ai = ADMUTIL_Init(); int ret_val = 0; char *startcmd = 0; AdmldapInfo info; + char *method = NULL; + char *redir = NULL; char *configdir = util_get_conf_dir(); + (void)_ai; /* get rid of unused variable warning */ + method = getenv("REQUEST_METHOD"); /* find and open the AS config file adm.conf */ info = admldapBuildInfoOnly(configdir, &ret_val); @@ -82,6 +87,12 @@ main(int argc, char *argv[]) if (ret_val != 0) return error_exit("Execution of the StartConfigDS command returned non-zero"); + if (method && !strcmp(method, "POST") && !post_begin(stdin) && + (redir = get_cgi_var("redir_to", NULL, NULL))) { + printf("Content-Type: text/html\nLocation: %s\n\n", redir); + exit(0); + } + return success_exit(); } diff --git a/mod_admserv/mod_admserv.c b/mod_admserv/mod_admserv.c index eacf1e8..d6d2e19 100644 --- a/mod_admserv/mod_admserv.c +++ b/mod_admserv/mod_admserv.c @@ -110,6 +110,7 @@ #define RQ_NOTES_SIEPWD "siepwd" #define RQ_NOTES_COMMAND_NAME "command-name" #define RQ_NOTES_AUTHZ_REQUIRED "authz-required" +#define RQ_NOTES_CONFIGDSDOWN "configdsdown" #define RUNTIME_COMMAND_BASE (char*)"commands/" #define AUTH_URI "/admin-serv/authenticate" #define MOD_ADMSERV_CONFIG_KEY "mod_admserv" @@ -682,7 +683,7 @@ admserv_ldap_auth_userdn_password(LDAP *server, if (ldapError) { ap_log_error(APLOG_MARK, APLOG_ERR, 0 /* status */, NULL, "Could not bind as [%s]: ldap error %d: %s", - userdn, ldapError, ldap_err2string(ldapError)); + userdn ? userdn : "(anon)", ldapError, ldap_err2string(ldapError)); return ldapError; } @@ -693,7 +694,7 @@ admserv_ldap_auth_userdn_password(LDAP *server, *pw_expiring = 0; ap_log_error(APLOG_MARK, APLOG_WARNING, 0 /* status */, NULL, "The password for user DN [%s] has expired - please reset it", - userdn); + userdn ? userdn : "(anon)"); } else if(!(strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_PWEXPIRING))) { /* "The password is expiring in n seconds" */ @@ -702,7 +703,7 @@ admserv_ldap_auth_userdn_password(LDAP *server, *pw_expiring = atoi(ctrls[i]->ldctl_value.bv_val); ap_log_error(APLOG_MARK, APLOG_WARNING, 0 /* status */, NULL, "The password for user DN [%s] will expire in %d seconds", - userdn, *pw_expiring); + userdn ? userdn : "(anon)", *pw_expiring); } } } @@ -847,6 +848,8 @@ buildUGInfo(char** errorInfo, const request_rec *r) { "buildUGInfo(): unable to initialize TLS connection to LDAP host %s port %d: %d", host, admldapGetPort(info), error); PL_strfree(host); + apr_table_set(r->notes, RQ_NOTES_CONFIGDSDOWN, apr_pstrdup(module_pool, "1")); + goto done; } @@ -1039,11 +1042,11 @@ admserv_error_std(request_rec *r, char *reason) } static int -check_auth_tasks_cache(char *dn, const char *userdn, request_rec *r, long now, int send_response) +check_auth_tasks_cache(char *dn, const char *userdn, request_rec *r, long now, int send_response, char **retmsg) { TaskCacheEntry *cache_entry; char normEntryDN[1024]; - long createTime; + long createTime = 0; char *msg; adm_normalize_dn(dn, normEntryDN); @@ -1055,7 +1058,7 @@ check_auth_tasks_cache(char *dn, const char *userdn, request_rec *r, long now, i normEntryDN); goto bad; } - if (!(createTime = (long)HashTableFind(cache_entry->auth_userDNs, userdn))) { + if (userdn && !(createTime = (long)HashTableFind(cache_entry->auth_userDNs, userdn))) { msg = apr_psprintf(r->pool, "check_auth_tasks_cache: found task [%s] but user [%s] is not authorized", dn, userdn); @@ -1064,7 +1067,7 @@ check_auth_tasks_cache(char *dn, const char *userdn, request_rec *r, long now, i if ((now - createTime) > cacheLifetime) { msg = apr_psprintf(r->pool, "check_auth_tasks_cache: task [%s] user [%s] entry has expired %ld", - dn, userdn, (now - createTime)); + dn, userdn ? userdn : "(anon)", (now - createTime)); goto bad; } @@ -1079,6 +1082,8 @@ bad: ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "%s", msg); if (send_response) { return admserv_error_std(r, msg); + } else if (retmsg) { + *retmsg = msg; } return DONE; @@ -1119,7 +1124,7 @@ sync_task_sie_data(const char *name, char *query, void *arg, request_rec *r) replace the siedn with the userdn - fortunately it doesn't use the siedn as the SIE DN */ admldapSetSIEDN(ldapInfo, userdn); - if (NULL == passwd) { /* use the passwd in cache if possible */ + if (userdn && (NULL == passwd)) { /* use the passwd in cache if possible */ cache_entry = (UserCacheEntry*)HashTableFind(auth_users, userdn); if (cache_entry) { passwd = cache_entry->userPW; @@ -1276,14 +1281,14 @@ task_update_registry_server_bindpw(char *uid, char *password, /* authenticate failed: Should not continue */ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "task_update_registry_server_bindpw(): failed to authenticate as %s: %s", - userDN, ldap_err2string(ldapError)); + userDN ? userDN : "(anon)", ldap_err2string(ldapError)); goto bailout; case LDAP_NO_SUCH_OBJECT: case LDAP_ALIAS_PROBLEM: case LDAP_INVALID_DN_SYNTAX: ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "task_update_registry_server_bindpw(): bad userdn %s: %s", - userDN, ldap_err2string(ldapError)); + userDN ? userDN : "(anon)", ldap_err2string(ldapError)); goto bailout; default: ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, @@ -1314,7 +1319,7 @@ task_update_registry_server_bindpw(char *uid, char *password, create_auth_users_cache_entry(uid, adminDN, password, ldapURL); /* We only want to reset the pwd in the request if RQ_NOTES_USERDN is the admin user */ - if(strcasecmp(adminDN, userDN) == 0) { + if(userDN && (strcasecmp(adminDN, userDN) == 0)) { apr_table_set(r->notes, RQ_NOTES_USERPW, password); } @@ -1502,6 +1507,7 @@ populate_tasks_from_server(char *serverid, const void *sieDN, void *userdata) char *execRefArgs = NULL; struct berval **vals, **vals2; TaskCacheEntry *cache_entry; + char *userdn; dn = ldap_get_dn(server, e); @@ -1542,15 +1548,18 @@ populate_tasks_from_server(char *serverid, const void *sieDN, void *userdata) } cache_entry->execRefArgs = execRefArgs ? apr_pstrdup(module_pool, execRefArgs) : NULL; cache_entry->logSuppress = (vals2 && vals[0] && !strncasecmp(vals2[0]->bv_val, LOG_SUPPRESS_ON_VALUE, vals2[0]->bv_len)); - HashTableInsert(cache_entry->auth_userDNs, apr_pstrdup(module_pool, data->userDN), - (char*)(data->now)); - + if (data->userDN) { + userdn = apr_pstrdup(module_pool, data->userDN); + } else { + userdn = apr_pstrdup(module_pool, ""); + } + HashTableInsert(cache_entry->auth_userDNs, userdn, (char*)(data->now)); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "populate_tasks_from_server(): Added task entry [%s:%s:%s] for user [%s]", normDN, cache_entry->execRef, cache_entry->execRefArgs ? cache_entry->execRefArgs : "", - data->userDN); + userdn); ldap_value_free_len(vals); if (vals2) @@ -1575,22 +1584,29 @@ populate_task_cache_entries(const char *userDN, LDAP *server) HashTableEnumerate(servers, populate_tasks_from_server, &data); } +#define STARTDS_IDENTIFIER "tasks/operation/StartConfigDS" +#define STARTDS_CGI "start_config_ds" +#define IS_START_CONFIG_DS_REQ(uri, serverid, userdn, user) \ + uri && !STRNCASECMP(uri, STARTDS_IDENTIFIER, strlen(STARTDS_IDENTIFIER)) && \ + serverid && !STRCMP(serverid, "admin-serv") && /* is local superuser */ !userdn && user + static int admserv_check_authz(request_rec *r) { int ldapError; char entryDN[LINE_LENGTH]; char *p; - char *siedn; /* this is looked up from the serverid, which is + char *siedn = NULL; /* this is looked up from the serverid, which is extracted from the uri */ - LDAP *server; - const char *userdn; - const char *pw; - char *serverid; + LDAP *server = NULL; + const char *userdn = NULL; + const char *pw = NULL; + char *serverid = NULL; char *storage = entryDN; - char *uri; - char *saveduri; + char *uri = NULL; + char *saveduri = NULL; long now; + int is_start_config_ds_req = 0; int tries = 0; admserv_config *cf = ap_get_module_config(r->per_dir_config, &admserv_module); @@ -1621,6 +1637,12 @@ admserv_check_authz(request_rec *r) saveduri = apr_pstrdup(r->pool, uri); *p = '\0'; + userdn = apr_table_get(r->notes, RQ_NOTES_USERDN); + pw = apr_table_get(r->notes, RQ_NOTES_USERPW); + + if (IS_START_CONFIG_DS_REQ(uri, serverid, userdn, r->user)) { + is_start_config_ds_req = 1; + } if (!(siedn = (char*)HashTableFind(servers, serverid))) { /* DT 4/6/98 -- If we're seeing a serverid for the first time, then we try to do * a resync to pull in new data for the serverid. If it still fails, @@ -1629,10 +1651,12 @@ admserv_check_authz(request_rec *r) admserv_runtime_command_exec(RUNTIME_RESYNC_COMMAND, r->args, r); if (!(siedn = (char*)HashTableFind(servers, serverid))) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "admserv_check_authz(): unable to find registered server (%s)", - serverid); - return admserv_error(r, HTTP_BAD_REQUEST, "server not registered"); /*i18n*/ + if (!is_start_config_ds_req) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "admserv_check_authz(): unable to find registered server (%s)", + serverid); + return admserv_error(r, HTTP_BAD_REQUEST, "server not registered"); /*i18n*/ + } } } @@ -1644,7 +1668,8 @@ admserv_check_authz(request_rec *r) return OK; } - if (!build_full_DN(&storage, entryDN+LINE_LENGTH, uri, siedn)) { + entryDN[0] = '\0'; + if (siedn && (!build_full_DN(&storage, entryDN+LINE_LENGTH, uri, siedn))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "admserv_check_authz(): unable to build DN from URL - bad URL [%s]", uri); @@ -1653,20 +1678,26 @@ admserv_check_authz(request_rec *r) convert_to_lower_case(entryDN); - userdn = apr_table_get(r->notes, RQ_NOTES_USERDN); - pw = apr_table_get(r->notes, RQ_NOTES_USERPW); - /* * if we got here, we either have a userdn - user was authenticated via LDAP * or r->user is set and was therefore authenticated via mod_auth - if both * LDAP and mod_auth auth fail, we should not be here - not an authenticated user */ if (!userdn && r->user) { - int retval = check_auth_tasks_cache(entryDN, LOCAL_SUPER_NAME, r, 0, 1 /* send_response */); + const char *configdsdown = apr_table_get(r->notes, RQ_NOTES_CONFIGDSDOWN); + int send_response = configdsdown ? 0 : 1; /* do not send response if configds is down - let handler */ + char *retmsg = NULL; + int retval = check_auth_tasks_cache(entryDN, LOCAL_SUPER_NAME, r, 0, send_response, &retmsg); if (retval != OK) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "admserv_check_authz: task [%s] not cached for local superuser", entryDN); + if (configdsdown) { + return OK; /* let the admserv_config_ds_down handler handle this situation */ + } + if (!send_response) { + return admserv_error_std(r, retmsg); + } return retval; } goto found; @@ -1684,7 +1715,7 @@ admserv_check_authz(request_rec *r) now = time(0); - if (check_auth_tasks_cache(entryDN, userdn, r, now, 0 /* no response */) == OK) { + if (check_auth_tasks_cache(entryDN, userdn, r, now, 0 /* no response */, NULL) == OK) { goto found; } @@ -1711,7 +1742,7 @@ admserv_check_authz(request_rec *r) if(!(server = openLDAPConnection(®istryServer))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "admserv_check_authz(): unable to LDAP BIND as [%s] to %s:%d", - userdn, registryServer.host, registryServer.port); + userdn ? userdn : "(anon)", registryServer.host, registryServer.port); return admserv_error_std(r, "unable to open LDAPConnection"); /*i18n*/ } } while (server != NULL && ++tries < 2); @@ -1720,11 +1751,11 @@ admserv_check_authz(request_rec *r) closeLDAPConnection(server); if ((ldapError == LDAP_CONNECT_ERROR) || (ldapError == LDAP_SERVER_DOWN)) { - int retval = check_auth_tasks_cache(entryDN, userdn, r, 0, 1 /* send response */); /* Try the cache, ignoring expiration */ + int retval = check_auth_tasks_cache(entryDN, userdn, r, 0, 1 /* send response */, NULL); /* Try the cache, ignoring expiration */ if (retval != OK) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "admserv_check_authz: could not find cached task [%s] for [%s]", - entryDN, userdn); + entryDN, userdn ? userdn : "(anon)"); return retval; } goto found; @@ -1732,7 +1763,7 @@ admserv_check_authz(request_rec *r) ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "admserv_check_authz(): got LDAP error %d talking to %s:%d - possible user [%s] not authorized", - ldapError, registryServer.host, registryServer.port, userdn); + ldapError, registryServer.host, registryServer.port, userdn ? userdn : "(anon)"); return admserv_error(r, HTTP_UNAUTHORIZED, "invalid user credentials"); /*i18n*/ } @@ -1742,14 +1773,14 @@ admserv_check_authz(request_rec *r) populate_task_cache_entries(userdn, server); closeLDAPConnection(server); - if (check_auth_tasks_cache(entryDN, userdn, r, now, 0 /* no response */) == OK) { + if (check_auth_tasks_cache(entryDN, userdn, r, now, 0 /* no response */, NULL) == OK) { goto found; } ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, "admserv_check_authz(): Task [%s] not found for user [%s] - either" " the task was not registered or the user was not authorized", - entryDN, userdn); + entryDN, userdn ? userdn : "(anon)"); return admserv_error(r, HTTP_UNAUTHORIZED, "task not found or unauthorized"); /*i18n*/ /* This is remapping - converting the requested URI to the actual CGI filename @@ -1762,12 +1793,7 @@ found: * Treat StartConfigDS as a special task. This will be the case if the user was * authenticated as the local superuser (admpw) and the server-id is "admin-serv" */ -# define STARTDS_IDENTIFIER "tasks/operation/StartConfigDS" -# define STARTDS_CGI "start_config_ds" - if (!STRNCASECMP(saveduri, STARTDS_IDENTIFIER, strlen(STARTDS_IDENTIFIER)) && - !STRCMP(serverid, "admin-serv") && - /* is local superuser */ - !userdn && r->user) { + if (is_start_config_ds_req) { apr_status_t status; if (!cf->cgibindir) { @@ -2620,6 +2646,7 @@ static const char * set_version_string(cmd_parms *cmd, void *dconf, const char * static int userauth(request_rec *r) { char *dummy = NULL; + const char *userdn = NULL; if (strcmp(r->handler, "user-auth")) return DECLINED; @@ -2633,12 +2660,15 @@ static int userauth(request_rec *r) buildUGInfo(&dummy, r); } - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "userauth, bind %s", - apr_table_get(r->notes, RQ_NOTES_USERDN)); + userdn = apr_table_get(r->notes, RQ_NOTES_USERDN); + if (!userdn) { + userdn = "(anon)"; + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "userauth, bind %s", userdn); ap_set_content_type(r, "text/html"); - ap_rprintf(r, "UserDN: %s\n", apr_table_get(r->notes, RQ_NOTES_USERDN)); + ap_rprintf(r, "UserDN: %s\n", userdn); ap_rprintf(r, "UserDirectory: ldap%s://%s:%d/%s\n", userGroupServer.secure ? "s" : "", userGroupServer.host, userGroupServer.port, @@ -2847,6 +2877,31 @@ admserv_command_handler(request_rec *r) return DONE; } +static void +show_start_config_ds_page(request_rec *r) +{ + ap_set_content_type(r, "text/html"); + ap_rputs(DOCTYPE_HTML_3_2, r); + ap_rputs("Configuration Directory Server is down", r); + ap_rputs("", r); + ap_rputs("
", r); + ap_rprintf(r, "", "/dist/download"); + ap_rputs("The Configuration Directory Server is down. To restart, click here: ", r); + ap_rputs("", r); + ap_rputs("
", r); +} + +static int +admserv_config_ds_down(request_rec *r) +{ + if (apr_table_get(r->notes, RQ_NOTES_CONFIGDSDOWN)) { + show_start_config_ds_page(r); + return DONE; + } + + return DECLINED; +} + static int fixup_adminsdk(request_rec *r) { admserv_config *cf = ap_get_module_config(r->per_dir_config, @@ -2994,6 +3049,7 @@ static void register_hooks(apr_pool_t *p) ap_hook_fixups(fixup_admin_server_header, NULL, NULL, APR_HOOK_MIDDLE); /* do per forked child init */ ap_hook_child_init(admserv_init_child, NULL,NULL, APR_HOOK_MIDDLE); + ap_hook_handler(admserv_config_ds_down, NULL, NULL, APR_HOOK_LAST); } static const command_rec mod_adm_cmds[] = -- 1.7.1