diff options
author | Noriko Hosoi <nhosoi@redhat.com> | 2006-01-10 23:53:08 +0000 |
---|---|---|
committer | Noriko Hosoi <nhosoi@redhat.com> | 2006-01-10 23:53:08 +0000 |
commit | 67385d898398f60886047b9239d027cf997f9433 (patch) | |
tree | c7f9daeed093bdef2afa6f809c13cdfb457a94c7 | |
parent | 4120eda907e600d7d4b7c3b54e747db8284b4037 (diff) | |
download | ds-67385d898398f60886047b9239d027cf997f9433.tar.gz ds-67385d898398f60886047b9239d027cf997f9433.tar.xz ds-67385d898398f60886047b9239d027cf997f9433.zip |
[174776] Multiple restores from a non-existant directory could wipe out database
If the specified backup dir does not exist, does not contain necessary files
(including the directory is not accessible), is not a directory, or is
identical to the path to the db dir, issues an error and stops restoring before
wiping out the database.
-rw-r--r-- | ldap/servers/slapd/back-ldbm/dblayer.c | 219 | ||||
-rw-r--r-- | ldap/servers/slapd/proto-slap.h | 2 | ||||
-rw-r--r-- | ldap/servers/slapd/util.c | 159 |
3 files changed, 248 insertions, 132 deletions
diff --git a/ldap/servers/slapd/back-ldbm/dblayer.c b/ldap/servers/slapd/back-ldbm/dblayer.c index 0d7cc703..da3f988f 100644 --- a/ldap/servers/slapd/back-ldbm/dblayer.c +++ b/ldap/servers/slapd/back-ldbm/dblayer.c @@ -799,16 +799,12 @@ void dblayer_sys_pages(size_t *pagesize, size_t *pages, size_t *procpages, size_ #if defined ( hpux ) { struct pst_static pst; - struct pst_dynamic pst_dyn; int rval = pstat_getstatic(&pst, sizeof(pst), (size_t)1, 0); if (rval < 0) /* pstat_getstatic failed */ return; *pagesize = pst.page_size; *pages = pst.physical_memory; *availpages = dblayer_getvirtualmemsize() / *pagesize; - rval = pstat_getdynamic(&pst_dyn, sizeof(pst_dyn), (size_t)1, 0); - if (rval < 0) /* pstat_getdynamic failed */ - return; if (procpages) { #define BURST (size_t)32 /* get BURST proc info at one time... */ @@ -2386,6 +2382,7 @@ int dblayer_post_close(struct ldbminfo *li, int dbmode) priv->dblayer_env = NULL; /* pEnv is now garbage */ +#if 0 /* DBDB do NOT remove the environment: bad, bad idea */ if (return_value == 0) { DB_ENV *env = 0; return_value = db_env_create(&env, 0); @@ -2397,9 +2394,7 @@ int dblayer_post_close(struct ldbminfo *li, int dbmode) if (return_value == 0) { char *home_dir = dblayer_get_home_dir(li, NULL); if (home_dir) - /* DBDB do NOT remove the environment: bad, bad idea - * return_value = env->remove(env, home_dir, 0); - */ + return_value = env->remove(env, home_dir, 0); if (0 == return_value && !((DBLAYER_ARCHIVE_MODE|DBLAYER_EXPORT_MODE) & dbmode) && !priv->dblayer_bad_stuff_happened) { @@ -2415,6 +2410,7 @@ int dblayer_post_close(struct ldbminfo *li, int dbmode) } } } +#endif return return_value; } @@ -4451,7 +4447,7 @@ int dblayer_copy_directory(struct ldbminfo *li, int *cnt, int instance_dir_flag, int indexonly, - int resetlsns) + int resetlsns) { dblayer_private *priv = NULL; char *new_src_dir = NULL; @@ -4593,14 +4589,14 @@ int dblayer_copy_directory(struct ldbminfo *li, } /* copy filename1 to filename2 */ - /* If the file is a database file, and resetlsns is set, then we need to do a key by key copy */ - if (strcmp(LDBM_FILENAME_SUFFIX, last_four_chars(filename1)) == 0 && resetlsns) { - return_value = dblayer_copy_file_resetlsns(src_dir, filename1, filename2, + /* If the file is a database file, and resetlsns is set, then we need to do a key by key copy */ + if (strcmp(LDBM_FILENAME_SUFFIX, last_four_chars(filename1)) == 0 && resetlsns) { + return_value = dblayer_copy_file_resetlsns(src_dir, filename1, filename2, 0, priv); - } else { - return_value = dblayer_copyfile(filename1, filename2, + } else { + return_value = dblayer_copyfile(filename1, filename2, 0, priv->dblayer_file_mode); - } + } slapi_ch_free((void**)&filename1); slapi_ch_free((void**)&filename2); if (0 > return_value) @@ -4751,11 +4747,13 @@ int dblayer_backup(struct ldbminfo *li, char *dest_dir, Slapi_Task *task) /* compare: make sure everything in list A is still in list B */ ok = 1; - for (listi = listA; *listi && ok; listi++) { + for (listi = listA; listi && *listi && ok; listi++) { int found = 0; - for (listj = listB; *listj && !found; listj++) { - if (strcmp(*listi, *listj) == 0) + for (listj = listB; listj && *listj && !found; listj++) { + if (strcmp(*listi, *listj) == 0) { found = 1; + break; + } } if (! found) { ok = 0; /* missing log: start over */ @@ -4775,7 +4773,7 @@ int dblayer_backup(struct ldbminfo *li, char *dest_dir, Slapi_Task *task) } if (ok) { - size_t p1len, p2len; + size_t p1len, p2len; char **listptr; prefix = NULL; @@ -4786,12 +4784,12 @@ int dblayer_backup(struct ldbminfo *li, char *dest_dir, Slapi_Task *task) prefix = home_dir; } /* log files have the same filename len(100 is a safety net:) */ - p1len = strlen(prefix) + strlen(*listB) + 100; + p1len = strlen(prefix) + strlen(*listB) + 100; pathname1 = (char *)slapi_ch_malloc(p1len); - p2len = strlen(dest_dir) + strlen(*listB) + 100; + p2len = strlen(dest_dir) + strlen(*listB) + 100; pathname2 = (char *)slapi_ch_malloc(p2len); /* We copy those over */ - for (listptr = listB; (*listptr) && ok; ++listptr) { + for (listptr = listB; listptr && *listptr && ok; ++listptr) { PR_snprintf(pathname1, p1len, "%s/%s", prefix, *listptr); PR_snprintf(pathname2, p2len, "%s/%s", dest_dir, *listptr); LDAPDebug(LDAP_DEBUG_ANY, "Backing up file %d (%s)\n", @@ -5172,8 +5170,9 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task, char * int dbmode; int action = 0; char *home_dir = NULL; - char *real_src_dir = NULL; - int frirestore = 0; /* Is a an FRI/single instance restore. 0 for no, 1 for yes */ + char *real_src_dir = NULL; + int frirestore = 0; /* Is a an FRI/single instance restore. 0 for no, 1 for yes */ + struct stat sbuf; PR_ASSERT(NULL != li); priv = (dblayer_private*)li->li_dblayer_private; @@ -5188,7 +5187,7 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task, char * PR_Unlock(li->li_config_mutex); home_dir = dblayer_get_home_dir(li, NULL); - + if (NULL == home_dir) { LDAPDebug(LDAP_DEBUG_ANY, @@ -5200,54 +5199,82 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task, char * /* If it is, we fail */ /* We check on the source staging area, no point in going further if it * isn't there */ + if (stat(src_dir, &sbuf) < 0) { + LDAPDebug(LDAP_DEBUG_ANY, "restore: backup directory %s does not " + "exist.\n", src_dir, 0, 0); + if (task) { + slapi_task_log_notice(task, + "Backup directory %s does not exist.\n", src_dir); + } + return LDAP_UNWILLING_TO_PERFORM; + } else if (!S_ISDIR(sbuf.st_mode)) { + LDAPDebug(LDAP_DEBUG_ANY, "restore: backup directory %s is not " + "a directory.\n", src_dir, 0, 0); + if (task) { + slapi_task_log_notice(task, + "Backup directory %s is not a directory.\n", src_dir); + } + return LDAP_UNWILLING_TO_PERFORM; + } if (!dbversion_exists(li, src_dir)) { - LDAPDebug(LDAP_DEBUG_ANY, "restore: source directory %s does not " + LDAPDebug(LDAP_DEBUG_ANY, "restore: backup directory %s does not " "contain a complete backup\n", src_dir, 0, 0); - - if (task) { - slapi_task_log_notice(task, "Source directory %s does not " + slapi_task_log_notice(task, "Backup directory %s does not " "contain a complete backup", src_dir ); } + return LDAP_UNWILLING_TO_PERFORM; } - /* If this is a FRI restore, the bename will be non-NULL */ - if (bename != NULL) - frirestore = 1; + /* If this is a FRI restore, the bename will be non-NULL */ + if (bename != NULL) + frirestore = 1; - /* - * Check if the target is a superset of the backup. - * If not don't restore any db at all, otherwise - * the target will be crippled. - */ - dirhandle = PR_OpenDir(src_dir); - if (NULL != dirhandle) - { - while ((direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT)) - && direntry->name) - { + /* + * Check if the target is a superset of the backup. + * If not don't restore any db at all, otherwise + * the target will be crippled. + */ + dirhandle = PR_OpenDir(src_dir); + if (NULL != dirhandle) + { + while ((direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT)) + && direntry->name) + { PR_snprintf(filename1, MAXPATHLEN, "%s/%s", src_dir, direntry->name); - if(!frirestore || strcmp(direntry->name,bename)==0) - { + if(!frirestore || strcmp(direntry->name,bename)==0) + { tmp_rval = PR_GetFileInfo(filename1, &info); if (tmp_rval == PR_SUCCESS && PR_FILE_DIRECTORY == info.type) { - inst = ldbm_instance_find_by_name(li, (char *)direntry->name); - if ( inst == NULL) - { - LDAPDebug(LDAP_DEBUG_ANY, - "ERROR: target server has no %s configured\n", direntry->name, 0, 0); - if (task) { - slapi_task_log_notice(task, - "ERROR: target server has no %s configured\n", direntry->name); - } - PR_CloseDir(dirhandle); - return LDAP_UNWILLING_TO_PERFORM; - } - } - } - } - PR_CloseDir(dirhandle); - } + inst = ldbm_instance_find_by_name(li, (char *)direntry->name); + if ( inst == NULL) + { + LDAPDebug(LDAP_DEBUG_ANY, + "ERROR: target server has no %s configured\n", direntry->name, 0, 0); + if (task) { + slapi_task_log_notice(task, + "ERROR: target server has no %s configured\n", direntry->name); + } + PR_CloseDir(dirhandle); + return LDAP_UNWILLING_TO_PERFORM; + } + + if (slapd_comp_path(src_dir, inst->inst_parent_dir_name) + == 0) { + LDAPDebug(LDAP_DEBUG_ANY, + "ERROR: backup dir %s and target dir %s are identical\n", src_dir, inst->inst_parent_dir_name, 0); + if (task) { + slapi_task_log_notice(task, + "ERROR: backup dir %s and target dir %s are identical\n", src_dir, inst->inst_parent_dir_name); + } + PR_CloseDir(dirhandle); + return LDAP_UNWILLING_TO_PERFORM; + } + } + } + } + PR_CloseDir(dirhandle); + } /* We delete the existing database */ @@ -5256,20 +5283,20 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task, char * return return_value; } - if (frirestore) /*if we are restoring a single backend*/ - { - char *new_src_dir = NULL; - return_value = dblayer_fri_restore(home_dir,src_dir,priv,task,&new_src_dir,bename); - if (return_value) { - return return_value; - } - /* Now modify the src_dir to point to our recovery area and carry on as if nothing had happened... */ - real_src_dir = new_src_dir; - } else - { - /* Otherwise use the src_dir from the caller */ - real_src_dir = src_dir; - } + if (frirestore) /*if we are restoring a single backend*/ + { + char *new_src_dir = NULL; + return_value = dblayer_fri_restore(home_dir,src_dir,priv,task,&new_src_dir,bename); + if (return_value) { + return return_value; + } + /* Now modify the src_dir to point to our recovery area and carry on as if nothing had happened... */ + real_src_dir = new_src_dir; + } else + { + /* Otherwise use the src_dir from the caller */ + real_src_dir = src_dir; + } /* We copy the files over from the staging area */ /* We want to treat the logfiles specially: if there's @@ -5300,14 +5327,14 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task, char * * directory is located. */ - inst = ldbm_instance_find_by_name(li, (char *)direntry->name); - if (inst == NULL) - continue; + inst = ldbm_instance_find_by_name(li, (char *)direntry->name); + if (inst == NULL) + continue; restore_dir = inst->inst_parent_dir_name; - /* If we're doing a partial restore, we need to reset the LSNs on the data files */ + /* If we're doing a partial restore, we need to reset the LSNs on the data files */ if (dblayer_copy_directory(li, task, filename1, - restore_dir, 1 /* restore */, &cnt, 0, 0, (bename) ? 1 : 0) == 0) + restore_dir, 1 /* restore */, &cnt, 0, 0, (bename) ? 1 : 0) == 0) continue; else { @@ -5418,7 +5445,7 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task, char * slapi_task_log_notice(task, "Failed to init database"); } return_value = tmp_rval; - goto error_out; + goto error_out; } if (0 == return_value) { /* only when the copyfile succeeded */ @@ -5443,21 +5470,21 @@ int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task, char * return_value = tmp_rval?tmp_rval:return_value; error_out: - /* Free the restore src dir, but only if we allocated it above */ - if (real_src_dir != src_dir) { - /* If this was an FRI restore and the staging area exists, go ahead and remove it */ - if (frirestore && PR_Access(real_src_dir, PR_ACCESS_EXISTS) == PR_SUCCESS) - { - int ret1 = 0; - LDAPDebug(LDAP_DEBUG_ANY, "dblayer_restore: Removing staging area %s.\n",real_src_dir, 0, 0); - ret1 = ldbm_delete_dirs(real_src_dir); - if (ret1) - { - LDAPDebug(LDAP_DEBUG_ANY, "dblayer_restore: Removal of staging area %s failed!\n", real_src_dir, 0, 0); - } - } - slapi_ch_free((void**)&real_src_dir); - } + /* Free the restore src dir, but only if we allocated it above */ + if (real_src_dir != src_dir) { + /* If this was an FRI restore and the staging area exists, go ahead and remove it */ + if (frirestore && PR_Access(real_src_dir, PR_ACCESS_EXISTS) == PR_SUCCESS) + { + int ret1 = 0; + LDAPDebug(LDAP_DEBUG_ANY, "dblayer_restore: Removing staging area %s.\n",real_src_dir, 0, 0); + ret1 = ldbm_delete_dirs(real_src_dir); + if (ret1) + { + LDAPDebug(LDAP_DEBUG_ANY, "dblayer_restore: Removal of staging area %s failed!\n", real_src_dir, 0, 0); + } + } + slapi_ch_free((void**)&real_src_dir); + } return return_value; } diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h index d1fdd406..1b1eaa5f 100644 --- a/ldap/servers/slapd/proto-slap.h +++ b/ldap/servers/slapd/proto-slap.h @@ -589,6 +589,8 @@ void g_set_accesslog_level(int val); void slapd_nasty(char* str, int c, int err); int strarray2str( char **a, char *buf, size_t buflen, int include_quotes ); int slapd_chown_if_not_owner(const char *filename, uid_t uid, gid_t gid); +int slapd_comp_path(char *p0, char *p1); + /* * modify.c diff --git a/ldap/servers/slapd/util.c b/ldap/servers/slapd/util.c index a129c6cc..a531e94c 100644 --- a/ldap/servers/slapd/util.c +++ b/ldap/servers/slapd/util.c @@ -44,6 +44,7 @@ #include <unistd.h> #include <pwd.h> #endif +#include <libgen.h> #include <pk11func.h> #include "slap.h" #include "prtime.h" @@ -53,6 +54,14 @@ #define UTIL_ESCAPE_HEX 1 #define UTIL_ESCAPE_BACKSLASH 2 +#if defined( _WIN32 ) +#define _PSEP "\\" +#define _CSEP '\\' +#else +#define _PSEP "/" +#define _CSEP '/' +#endif + static int special_np(unsigned char c) { if(c < 32 || c > 126) { @@ -383,6 +392,60 @@ is_abspath(const char *path) return 0; /* not an abs path */ } +static void +clean_path(char **norm_path) +{ + char **np; + + for (np = norm_path; np && *np; np++) + slapi_ch_free((void **)np); + slapi_ch_free((void **)&norm_path); +} + +static char ** +normalize_path(char *path) +{ + char *dname = slapi_ch_strdup(path); + char *dnamep = dname; + char *bnamep = NULL; + char **dirs = (char **)slapi_ch_calloc(strlen(path), 1); + char **rdirs = (char **)slapi_ch_calloc(strlen(path), 1); + char **dp = dirs; + char **rdp; + do { + bnamep = basename(dnamep); + if (0 != strcmp(bnamep, ".")) { + *dp++ = slapi_ch_strdup(bnamep); /* remove "/./" in the path */ + } + dnamep = dirname(dnamep); + } while (strcmp(dnamep, _PSEP) && + !(0 == strcmp(dnamep, ".") && 0 == strcmp(bnamep, "."))); + + /* remove "xxx/.." in the path */ + for (dp = dirs, rdp = rdirs; dp && *dp; dp++) { + while (*dp && 0 == strcmp(*dp, "..")) { + dp++; + if (rdp > rdirs) + rdp--; + } + if (*dp) + *rdp++ = slapi_ch_strdup(*dp); + } + for (--dp, rdp = rdirs; dp >= dirs; dp--) { + while (*dp && 0 == strcmp(*dp, "..")) { + dp--; + if (rdp > rdirs) + rdp--; + } + if (*dp) + *rdp++ = slapi_ch_strdup(*dp); + } + + clean_path(dirs); + slapi_ch_free_string(&dname); + + return rdirs; +} /* * Take "relpath" and prepend the current working directory to it @@ -399,13 +462,10 @@ rel2abspath( char *relpath ) CHAR szDir[_MAX_DIR]; CHAR szFname[_MAX_FNAME]; CHAR szExt[_MAX_EXT]; -#define _PSEP "\\" -#else -#define _PSEP "/" #endif if ( relpath == NULL ) { - return NULL; + return NULL; } #if defined( _WIN32 ) @@ -413,41 +473,51 @@ rel2abspath( char *relpath ) memset (&szDir, 0, sizeof (szDir)); memset (&szFname, 0, sizeof (szFname)); memset (&szExt, 0, sizeof (szExt)); - _splitpath( relpath, szDrive, szDir, szFname, szExt ); - if( szDrive[0] && szDir[0] ) - return( slapi_ch_strdup( relpath )); - if ( relpath[ 0 ] == '/' || relpath[ 0 ] == '\\' ) { - return( slapi_ch_strdup( relpath )); -#else - if ( relpath[ 0 ] == '/' ) { - return( slapi_ch_strdup( relpath )); - } + _splitpath( relpath, szDrive, szDir, szFname, szExt ); + if( szDrive[0] && szDir[0] ) + return( slapi_ch_strdup( relpath )); #endif - - if ( getcwd( abspath, MAXPATHLEN ) == NULL ) { - perror( "getcwd" ); - LDAPDebug( LDAP_DEBUG_ANY, "Cannot determine current directory\n", - 0, 0, 0 ); - exit( 1 ); - } - - if ( strlen( relpath ) + strlen( abspath ) + 1 > MAXPATHLEN ) { - LDAPDebug( LDAP_DEBUG_ANY, "Pathname \"%s" _PSEP "%s\" too long\n", - abspath, relpath, 0 ); - exit( 1 ); + if ( relpath[ 0 ] == _CSEP ) { /* absolute path */ + PR_snprintf(abspath, sizeof(abspath), "%s", relpath); + } else { /* relative path */ + if ( getcwd( abspath, MAXPATHLEN ) == NULL ) { + perror( "getcwd" ); + LDAPDebug( LDAP_DEBUG_ANY, "Cannot determine current directory\n", + 0, 0, 0 ); + exit( 1 ); + } + + if ( strlen( relpath ) + strlen( abspath ) + 1 > MAXPATHLEN ) { + LDAPDebug( LDAP_DEBUG_ANY, "Pathname \"%s" _PSEP "%s\" too long\n", + abspath, relpath, 0 ); + exit( 1 ); + } + + if ( strcmp( relpath, "." )) { + if ( abspath[ 0 ] != '\0' && + abspath[ strlen( abspath ) - 1 ] != _CSEP ) + { + PL_strcatn( abspath, sizeof(abspath), "/" ); + } + PL_strcatn( abspath, sizeof(abspath), relpath ); + } } - - if ( strcmp( relpath, "." )) { -#if defined( _WIN32 ) - if ( abspath[ 0 ] != '\0' && abspath[ strlen( abspath ) - 1 ] != '/' && abspath[ strlen( abspath ) - 1 ] != '\\' ) -#else - if ( abspath[ 0 ] != '\0' && abspath[ strlen( abspath ) - 1 ] != '/' ) { -#endif - PL_strcatn( abspath, sizeof(abspath), "/" ); - } - PL_strcatn( abspath, sizeof(abspath), relpath ); + { + char **norm_path = normalize_path(abspath); + char *retpath = slapi_ch_strdup(abspath); /* size is long enough */ + char **np, *rp; + int pathlen = strlen(abspath) + 1; + int usedlen = 0; + for (np = norm_path, rp = retpath; np && *np; np++) { + int thislen = strlen(*np) + 1; + if (0 != strcmp(*np, _PSEP)) + PR_snprintf(rp, pathlen - usedlen, "%c%s", _CSEP, *np); + rp += thislen; + usedlen += thislen; + } + clean_path(norm_path); + return retpath; } - return( slapi_ch_strdup( abspath )); } @@ -658,3 +728,20 @@ slapd_chown_if_not_owner(const char *filename, uid_t uid, gid_t gid) return result; } +/* + * Compare 2 pathes + * Paths could contain ".", "..", "//" in the path, thus normalize them first. + * One or two of the paths could be a relative path. + */ +int +slapd_comp_path(char *p0, char *p1) +{ + int rval = 0; + char *norm_p0 = rel2abspath(p0); + char *norm_p1 = rel2abspath(p1); + + rval = strcmp(norm_p0, norm_p1); + slapi_ch_free_string(&norm_p0); + slapi_ch_free_string(&norm_p1); + return rval; +} |