int CompressPath(char *dest, const char *src) { char node[CF_BUFSIZE]; int nodelen; int rootlen; memset(dest, 0, CF_BUFSIZE); rootlen = RootDirLength(src); strncpy(dest, src, rootlen); for (const char *sp = src + rootlen; *sp != '\0'; sp++) { if (IsFileSep(*sp)) { continue; } for (nodelen = 0; (sp[nodelen] != '\0') && (!IsFileSep(sp[nodelen])); nodelen++) { if (nodelen > CF_MAXLINKSIZE) { Log(LOG_LEVEL_ERR, "Link in path suspiciously large"); return false; } } strncpy(node, sp, nodelen); node[nodelen] = '\0'; sp += nodelen - 1; if (strcmp(node, ".") == 0) { continue; } if (strcmp(node, "..") == 0) { if (!ChopLastNode(dest)) { Log(LOG_LEVEL_DEBUG, "used .. beyond top of filesystem!"); return false; } continue; } else { AddSlash(dest); } if (!JoinPath(dest, node)) { return false; } } return true; }
static char *AbsLinkPath(const char *from, const char *relto) /* Take an abolute source and a relative destination object and find the absolute name of the to object */ { int pop = 1; static char destination[CF_BUFSIZE]; if (IsAbsoluteFileName(relto)) { ProgrammingError("Call to AbsLInkPath with absolute pathname"); } strcpy(destination, from); /* reuse to save stack space */ const char *sp = NULL; for (sp = relto; *sp != '\0'; sp++) { if (strncmp(sp, "../", 3) == 0) { pop++; sp += 2; continue; } if (strncmp(sp, "./", 2) == 0) { sp += 1; continue; } break; /* real link */ } while (pop > 0) { ChopLastNode(destination); pop--; } if (strlen(destination) == 0) { strcpy(destination, "/"); } else { AddSlash(destination); } strcat(destination, sp); Log(LOG_LEVEL_DEBUG, "Reconstructed absolute linkname '%s'", destination); return destination; }
/* * Take an abolute source and a relative destination object and find the * absolute name of the to object */ char * AbsLinkPath(char *from, char *relto) { char *sp; int pop = 1; static char destination[CF_BUFSIZE]; if (*relto == '/') { printf("Cfengine internal error: call to AbsLInkPath " "with absolute pathname\n"); FatalError(""); } strcpy(destination,from); /* reuse to save stack space */ for (sp = relto; *sp != '\0'; sp++) { if (strncmp(sp,"../",3) == 0) { pop++; sp += 2; continue; } if (strncmp(sp,"./",2) == 0) { sp += 1; continue; } break; /* real link */ } while (pop > 0) { ChopLastNode(destination); pop--; } if (strlen(destination) == 0) { strcpy(destination,"/"); } else { AddSlash(destination); } strcat(destination,sp); Debug("Reconstructed absolute linkname = %s\n",destination); return destination; }
/* 'resolved' argument needs to be at least CF_BUFSIZE long */ bool ResolveFilename(const char *req_path, char *res_path) { char req_dir[CF_BUFSIZE]; char req_filename[CF_BUFSIZE]; /* * Eliminate symlinks from path, but do not resolve the file itself if it is a * symlink. */ strlcpy(req_dir, req_path, CF_BUFSIZE); ChopLastNode(req_dir); strlcpy(req_filename, ReadLastNode(req_path), CF_BUFSIZE); #if defined HAVE_REALPATH && !defined _WIN32 if (realpath(req_dir, res_path) == NULL) { return false; } #else memset(res_path, 0, CF_BUFSIZE); CompressPath(res_path, req_dir); #endif AddSlash(res_path); strlcat(res_path, req_filename, CF_BUFSIZE); /* Adjust for forward slashes */ MapName(res_path); /* NT has case-insensitive path names */ #ifdef __MINGW32__ int i; for (i = 0; i < strlen(res_path); i++) { res_path[i] = ToLower(res_path[i]); } #endif /* __MINGW32__ */ return true; }
PromiseResult VerifyAbsoluteLink(EvalContext *ctx, char *destination, const char *source, Attributes attr, const Promise *pp) { char absto[CF_BUFSIZE]; char expand[CF_BUFSIZE]; char linkto[CF_BUFSIZE]; if (*source == '.') { strcpy(linkto, destination); ChopLastNode(linkto); AddSlash(linkto); strcat(linkto, source); } else { strcpy(linkto, source); } CompressPath(absto, linkto); expand[0] = '\0'; if (attr.link.when_no_file == cfa_force) { if (!ExpandLinks(expand, absto, 0)) /* begin at level 1 and beam out at 15 */ { Log(LOG_LEVEL_ERR, "Failed to make absolute link in"); PromiseRef(LOG_LEVEL_ERR, pp); return PROMISE_RESULT_FAIL; } else { Log(LOG_LEVEL_DEBUG, "ExpandLinks returned '%s'", expand); } } else { strcpy(expand, absto); } CompressPath(linkto, expand); return VerifyLink(ctx, destination, linkto, attr, pp); }
int AbsoluteLink(char *from, char *to, struct Item *inclusions, struct Item *exclusions, struct Item *copy, short nofile, struct Link *ptr) { char absto[CF_BUFSIZE]; char expand[CF_BUFSIZE]; Debug2("AbsoluteLink(%s,%s)\n",from,to); if (*to == '.') { strcpy(g_linkto,from); ChopLastNode(g_linkto); AddSlash(g_linkto); strcat(g_linkto,to); } else { strcpy(g_linkto,to); } CompressPath(absto,g_linkto); expand[0] = '\0'; if (!nofile) { /* begin at level 1 and beam out at 15 */ if (!ExpandLinks(expand,absto,0)) { CfLog(cferror,"Failed to make absolute link in\n",""); snprintf(g_output,CF_BUFSIZE*2,"%s -> %s\n",from,to); CfLog(cferror,g_output,""); return false; } else { Debug2("ExpandLinks returned %s\n",expand); } } else { strcpy(expand,absto); } CompressPath(g_linkto,expand); return LinkFiles(from,g_linkto,inclusions,exclusions,copy,nofile,ptr); }
static char *InputLocation(char *filename) { static char wfilename[CF_BUFSIZE], path[CF_BUFSIZE]; if (MINUSF && (filename != VINPUTFILE) && IsFileOutsideDefaultRepository(VINPUTFILE) && !IsAbsoluteFileName(filename)) { /* If -f assume included relative files are in same directory */ strncpy(path,VINPUTFILE,CF_BUFSIZE-1); ChopLastNode(path); snprintf(wfilename,CF_BUFSIZE-1,"%s%c%s",path,FILE_SEPARATOR,filename); } else if (IsFileOutsideDefaultRepository(filename)) { strncpy(wfilename,filename,CF_BUFSIZE-1); } else { snprintf(wfilename,CF_BUFSIZE-1,"%s%cinputs%c%s",CFWORKDIR,FILE_SEPARATOR,FILE_SEPARATOR,filename); } return MapName(wfilename); }
void GenericAgentConfigSetInputFile(GenericAgentConfig *config, const char *workdir, const char *input_file) { free(config->original_input_file); free(config->input_file); free(config->input_dir); config->original_input_file = xstrdup(input_file); if (workdir && FilePathGetType(input_file) == FILE_PATH_TYPE_NON_ANCHORED) { config->input_file = StringFormat("%s%cinputs%c%s", workdir, FILE_SEPARATOR, FILE_SEPARATOR, input_file); } else { config->input_file = xstrdup(input_file); } config->input_dir = xstrdup(config->input_file); if (!ChopLastNode(config->input_dir)) { free(config->input_dir); config->input_dir = xstrdup("."); } }
int ExpandLinks(char *dest, const char *from, int level) { char buff[CF_BUFSIZE]; char node[CF_MAXLINKSIZE]; struct stat statbuf; int lastnode = false; memset(dest, 0, CF_BUFSIZE); if (level >= CF_MAXLINKLEVEL) { Log(LOG_LEVEL_ERR, "Too many levels of symbolic links to evaluate absolute path"); return false; } const char *sp = from; while (*sp != '\0') { if (*sp == FILE_SEPARATOR) { sp++; continue; } sscanf(sp, "%[^/]", node); sp += strlen(node); if (*sp == '\0') { lastnode = true; } if (strcmp(node, ".") == 0) { continue; } if (strcmp(node, "..") == 0) { continue; } else { strcat(dest, "/"); } strcat(dest, node); if (lstat(dest, &statbuf) == -1) /* File doesn't exist so we can stop here */ { Log(LOG_LEVEL_ERR, "Can't stat '%s' in ExpandLinks. (lstat: %s)", dest, GetErrorStr()); return false; } if (S_ISLNK(statbuf.st_mode)) { memset(buff, 0, CF_BUFSIZE); if (readlink(dest, buff, CF_BUFSIZE - 1) == -1) { Log(LOG_LEVEL_ERR, "Expand links can't stat '%s'. (readlink: %s)", dest, GetErrorStr()); return false; } else { if (buff[0] == '.') { ChopLastNode(dest); AddSlash(dest); if (!JoinPath(dest, buff)) { return false; } } else if (IsAbsoluteFileName(buff)) { strcpy(dest, buff); DeleteSlash(dest); if (strcmp(dest, from) == 0) { Log(LOG_LEVEL_DEBUG, "No links to be expanded"); return true; } if ((!lastnode) && (!ExpandLinks(buff, dest, level + 1))) { return false; } } else { ChopLastNode(dest); AddSlash(dest); strcat(dest, buff); DeleteSlash(dest); if (strcmp(dest, from) == 0) { Log(LOG_LEVEL_DEBUG, "No links to be expanded"); return true; } memset(buff, 0, CF_BUFSIZE); if ((!lastnode) && (!ExpandLinks(buff, dest, level + 1))) { return false; } } } } } return true; }
int DepthSearch(char *name, struct stat *sb, int rlevel, Attributes attr, Promise *pp) { Dir *dirh; int goback; const struct dirent *dirp; char path[CF_BUFSIZE]; struct stat lsb; if (!attr.havedepthsearch) /* if the search is trivial, make sure that we are in the parent dir of the leaf */ { char basedir[CF_BUFSIZE]; CfDebug(" -> Direct file reference %s, no search implied\n", name); snprintf(basedir, sizeof(basedir), "%s", name); ChopLastNode(basedir); chdir(basedir); return VerifyFileLeaf(name, sb, attr, pp); } if (rlevel > CF_RECURSION_LIMIT) { CfOut(cf_error, "", "WARNING: Very deep nesting of directories (>%d deep): %s (Aborting files)", rlevel, name); return false; } if (rlevel > CF_RECURSION_LIMIT) { CfOut(cf_error, "", "WARNING: Very deep nesting of directories (>%d deep): %s (Aborting files)", rlevel, name); return false; } memset(path, 0, CF_BUFSIZE); CfDebug("To iterate is Human, to recurse is Divine...(%s)\n", name); if (!PushDirState(name, sb)) { return false; } if ((dirh = OpenDirLocal(".")) == NULL) { CfOut(cf_inform, "opendir", "Could not open existing directory %s\n", name); return false; } for (dirp = ReadDir(dirh); dirp != NULL; dirp = ReadDir(dirh)) { if (!ConsiderFile(dirp->d_name, name, attr, pp)) { continue; } strcpy(path, name); AddSlash(path); if (!JoinPath(path, dirp->d_name)) { CloseDir(dirh); return true; } if (lstat(dirp->d_name, &lsb) == -1) { CfOut(cf_verbose, "lstat", "Recurse was looking at %s when an error occurred:\n", path); continue; } if (S_ISLNK(lsb.st_mode)) /* should we ignore links? */ { if (!KillGhostLink(path, attr, pp)) { VerifyFileLeaf(path, &lsb, attr, pp); } else { continue; } } /* See if we are supposed to treat links to dirs as dirs and descend */ if (attr.recursion.travlinks && S_ISLNK(lsb.st_mode)) { if (lsb.st_uid != 0 && lsb.st_uid != getuid()) { CfOut(cf_inform, "", "File %s is an untrusted link: cfengine will not follow it with a destructive operation", path); continue; } /* if so, hide the difference by replacing with actual object */ if (cfstat(dirp->d_name, &lsb) == -1) { CfOut(cf_error, "stat", "Recurse was working on %s when this failed:\n", path); continue; } } if (attr.recursion.xdev && DeviceBoundary(&lsb, pp)) { CfOut(cf_verbose, "", "Skipping %s on different device - use xdev option to change this\n", path); continue; } if (S_ISDIR(lsb.st_mode)) { if (SkipDirLinks(path, dirp->d_name, attr.recursion)) { continue; } if (attr.recursion.depth > 1 && rlevel <= attr.recursion.depth) { CfOut(cf_verbose, "", " ->> Entering %s (%d)\n", path, rlevel); goback = DepthSearch(path, &lsb, rlevel + 1, attr, pp); PopDirState(goback, name, sb, attr.recursion); VerifyFileLeaf(path, &lsb, attr, pp); } else { VerifyFileLeaf(path, &lsb, attr, pp); } } else { VerifyFileLeaf(path, &lsb, attr, pp); } } CloseDir(dirh); return true; }
static PromiseResult VerifyFilePromise(EvalContext *ctx, char *path, Promise *pp) { struct stat osb, oslb, dsb; Attributes a = { {0} }; CfLock thislock; int exists; a = GetFilesAttributes(ctx, pp); if (!FileSanityChecks(ctx, path, a, pp)) { return PROMISE_RESULT_NOOP; } EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", path, DATA_TYPE_STRING); thislock = AcquireLock(ctx, path, VUQNAME, CFSTARTTIME, a.transaction, pp, false); if (thislock.lock == NULL) { return PROMISE_RESULT_NOOP; } LoadSetuid(a); PromiseResult result = PROMISE_RESULT_NOOP; if (lstat(path, &oslb) == -1) /* Careful if the object is a link */ { if ((a.create) || (a.touch)) { if (!CfCreateFile(ctx, path, pp, a, &result)) { goto exit; } else { exists = (lstat(path, &oslb) != -1); } } exists = false; } else { if ((a.create) || (a.touch)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "File '%s' exists as promised", path); } exists = true; } if ((a.havedelete) && (!exists)) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "File '%s' does not exist as promised", path); goto exit; } if (!a.havedepthsearch) /* if the search is trivial, make sure that we are in the parent dir of the leaf */ { char basedir[CF_BUFSIZE]; Log(LOG_LEVEL_DEBUG, "Direct file reference '%s', no search implied", path); snprintf(basedir, sizeof(basedir), "%s", path); if (strcmp(ReadLastNode(basedir), ".") == 0) { // Handle /. notation for deletion of directories ChopLastNode(basedir); ChopLastNode(path); } ChopLastNode(basedir); if (chdir(basedir)) { Log(LOG_LEVEL_ERR, "Failed to chdir into '%s'", basedir); } } if (exists && (!VerifyFileLeaf(ctx, path, &oslb, a, pp, &result))) { if (!S_ISDIR(oslb.st_mode)) { goto exit; } } if (stat(path, &osb) == -1) { if ((a.create) || (a.touch)) { if (!CfCreateFile(ctx, path, pp, a, &result)) { goto exit; } else { exists = true; } } else { exists = false; } } else { if (!S_ISDIR(osb.st_mode)) { if (a.havedepthsearch) { Log(LOG_LEVEL_WARNING, "depth_search (recursion) is promised for a base object '%s' that is not a directory", path); goto exit; } } exists = true; } if (a.link.link_children) { if (stat(a.link.source, &dsb) != -1) { if (!S_ISDIR(dsb.st_mode)) { Log(LOG_LEVEL_ERR, "Cannot promise to link the children of '%s' as it is not a directory!", a.link.source); goto exit; } } } /* Phase 1 - */ if (exists && ((a.havedelete) || (a.haverename) || (a.haveperms) || (a.havechange) || (a.transformer))) { lstat(path, &oslb); /* if doesn't exist have to stat again anyway */ DepthSearch(ctx, path, &oslb, 0, a, pp, oslb.st_dev, &result); /* normally searches do not include the base directory */ if (a.recursion.include_basedir) { int save_search = a.havedepthsearch; /* Handle this node specially */ a.havedepthsearch = false; DepthSearch(ctx, path, &oslb, 0, a, pp, oslb.st_dev, &result); a.havedepthsearch = save_search; } else { /* unless child nodes were repaired, set a promise kept class */ if (!IsDefinedClass(ctx, "repaired" , PromiseGetNamespace(pp))) { cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Basedir '%s' not promising anything", path); } } if (((a.change.report_changes) == FILE_CHANGE_REPORT_CONTENT_CHANGE) || ((a.change.report_changes) == FILE_CHANGE_REPORT_ALL)) { if (a.havedepthsearch) { PurgeHashes(ctx, NULL, a, pp); } else { PurgeHashes(ctx, path, a, pp); } } } /* Phase 2a - copying is potentially threadable if no followup actions */ if (a.havecopy) { result = PromiseResultUpdate(result, ScheduleCopyOperation(ctx, path, a, pp)); } /* Phase 2b link after copy in case need file first */ if ((a.havelink) && (a.link.link_children)) { result = PromiseResultUpdate(result, ScheduleLinkChildrenOperation(ctx, path, a.link.source, 1, a, pp)); } else if (a.havelink) { result = PromiseResultUpdate(result, ScheduleLinkOperation(ctx, path, a.link.source, a, pp)); } /* Phase 3 - content editing */ if (a.haveedit) { result = PromiseResultUpdate(result, ScheduleEditOperation(ctx, path, a, pp)); } // Once more in case a file has been created as a result of editing or copying exists = (stat(path, &osb) != -1); if (exists && (S_ISREG(osb.st_mode))) { VerifyFileLeaf(ctx, path, &osb, a, pp, &result); } if (!exists && a.havechange) { cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Promised to monitor '%s' for changes, but file does not exist", path); result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL); } exit: result = PromiseResultUpdate(result, SaveSetuid(ctx, a, pp)); YieldCurrentLock(thislock); return result; }
/** * @TODO fix the dangerous path lengths */ int CompressPath(char *dest, const char *src) { char node[CF_BUFSIZE]; int nodelen; int rootlen; memset(dest, 0, CF_BUFSIZE); rootlen = RootDirLength(src); memcpy(dest, src, rootlen); for (const char *sp = src + rootlen; *sp != '\0'; sp++) { if (IsFileSep(*sp)) { continue; } for (nodelen = 0; (sp[nodelen] != '\0') && (!IsFileSep(sp[nodelen])); nodelen++) { if (nodelen > CF_MAXLINKSIZE) { Log(LOG_LEVEL_ERR, "Link in path suspiciously large"); return false; } } strncpy(node, sp, nodelen); node[nodelen] = '\0'; sp += nodelen - 1; if (strcmp(node, ".") == 0) { continue; } if (strcmp(node, "..") == 0) { if (!ChopLastNode(dest)) { Log(LOG_LEVEL_DEBUG, "used .. beyond top of filesystem!"); return false; } continue; } AddSlash(dest); /* TODO use dest_size parameter instead of CF_BUFSIZE. */ size_t ret = strlcat(dest, node, CF_BUFSIZE); if (ret >= CF_BUFSIZE) { Log(LOG_LEVEL_ERR, "Internal limit reached in CompressPath()," " path too long: '%s' + '%s'", dest, node); return false; } } return true; }
int ExpandLinks(char *dest, char *from, int level) { char *sp, buff[CF_BUFSIZE]; char node[CF_MAXLINKSIZE]; struct stat statbuf; int lastnode = false; memset(dest,0,CF_BUFSIZE); Debug2("ExpandLinks(%s,%d)\n",from,level); if (level >= CF_MAXLINKLEVEL) { CfLog(cferror, "Too many levels of symbolic links to evaluate " "absolute path\n",""); return false; } for (sp = from; *sp != '\0'; sp++) { if (*sp == '/') { continue; } sscanf(sp,"%[^/]",node); sp += strlen(node); if (*sp == '\0') { lastnode = true; } if (strcmp(node,".") == 0) { continue; } if (strcmp(node,"..") == 0) { if (! ChopLastNode(g_linkto)) { Debug("cfagent: used .. beyond top of filesystem!\n"); return false; } continue; } else { strcat(dest,"/"); } strcat(dest,node); /* File doesn't exist so we can stop here */ if (lstat(dest,&statbuf) == -1) { snprintf(g_output, CF_BUFSIZE*2, "Can't stat %s in ExpandLinks\n", dest); CfLog(cferror,g_output,"stat"); return false; } if (S_ISLNK(statbuf.st_mode)) { memset(buff,0,CF_BUFSIZE); if (readlink(dest,buff,CF_BUFSIZE-1) == -1) { snprintf(g_output, CF_BUFSIZE*2, "Expand links can't stat %s\n", dest); CfLog(cferror,g_output,"readlink"); return false; } else { if (buff[0] == '.') { ChopLastNode(dest); AddSlash(dest); if (BufferOverflow(dest,buff)) { return false; } strcat(dest,buff); } else if (buff[0] == '/') { strcpy(dest,buff); DeleteSlash(dest); if (strcmp(dest,from) == 0) { Debug2("No links to be expanded\n"); return true; } if (!lastnode && !ExpandLinks(buff,dest,level+1)) { return false; } } else { ChopLastNode(dest); AddSlash(dest); strcat(dest,buff); DeleteSlash(dest); if (strcmp(dest,from) == 0) { Debug2("No links to be expanded\n"); return true; } memset(buff,0,CF_BUFSIZE); if (!lastnode && !ExpandLinks(buff,dest,level+1)) { return false; } } } } } return true; }
int CompressPath(char *dest,char *src) { char *sp; char node[CF_BUFSIZE]; int nodelen; int rootlen; Debug2("CompressPath(%s,%s)\n",dest,src); memset(dest,0,CF_BUFSIZE); rootlen = RootDirLength(src); strncpy(dest,src,rootlen); for (sp = src+rootlen; *sp != '\0'; sp++) { if (IsFileSep(*sp)) { continue; } for (nodelen = 0; sp[nodelen] != '\0' && !IsFileSep(sp[nodelen]); nodelen++) { if (nodelen > CF_MAXLINKSIZE) { CfLog(cferror,"Link in path suspiciously large",""); return false; } } strncpy(node, sp, nodelen); node[nodelen] = '\0'; sp += nodelen - 1; if (strcmp(node,".") == 0) { continue; } if (strcmp(node,"..") == 0) { if (!ChopLastNode(dest)) { Debug("cfengine: used .. beyond top of filesystem!\n"); return false; } continue; } else { AddSlash(dest); } if (BufferOverflow(dest,node)) { return false; } strcat(dest,node); } return true; }