void EvalContextHeapAddHard(EvalContext *ctx, const char *context) { char context_copy[CF_MAXVARSIZE]; strcpy(context_copy, context); if (Chop(context_copy, CF_EXPANDSIZE) == -1) { CfOut(OUTPUT_LEVEL_ERROR, "", "Chop was called on a string that seemed to have no terminator"); } CanonifyNameInPlace(context_copy); CfDebug("EvalContextHeapAddHard(%s)\n", context_copy); if (strlen(context_copy) == 0) { return; } if (IsRegexItemIn(ctx, ctx->heap_abort_current_bundle, context_copy)) { CfOut(OUTPUT_LEVEL_ERROR, "", "Bundle aborted on defined class \"%s\"\n", context_copy); ABORTBUNDLE = true; } if (IsRegexItemIn(ctx, ctx->heap_abort, context_copy)) { CfOut(OUTPUT_LEVEL_ERROR, "", "cf-agent aborted on defined class \"%s\"\n", context_copy); exit(1); } if (EvalContextHeapContainsHard(ctx, context_copy)) { return; } StringSetAdd(ctx->heap_hard, xstrdup(context_copy)); for (const Item *ip = ctx->heap_abort; ip != NULL; ip = ip->next) { if (IsDefinedClass(ctx, ip->name, NULL)) { CfOut(OUTPUT_LEVEL_ERROR, "", "cf-agent aborted on defined class \"%s\" defined in bundle %s\n", ip->name, StackFrameOwnerName(LastStackFrame(ctx, 0))); exit(1); } } if (!ABORTBUNDLE) { for (const Item *ip = ctx->heap_abort_current_bundle; ip != NULL; ip = ip->next) { if (IsDefinedClass(ctx, ip->name, NULL)) { CfOut(OUTPUT_LEVEL_ERROR, "", " -> Setting abort for \"%s\" when setting \"%s\"", ip->name, context_copy); ABORTBUNDLE = true; break; } } } }
int ScheduleAgentOperations(struct Bundle *bp) { struct SubType *sp; struct Promise *pp; enum typesequence type; int pass; if (PROCESSREFRESH == NULL || (PROCESSREFRESH && IsRegexItemIn(PROCESSREFRESH,bp->name))) { DeleteItemList(PROCESSTABLE); PROCESSTABLE = NULL; } for (pass = 1; pass < CF_DONEPASSES; pass++) { for (type = 0; TYPESEQUENCE[type] != NULL; type++) { ClassBanner(type); if ((sp = GetSubTypeForBundle(TYPESEQUENCE[type],bp)) == NULL) { continue; } BannerSubType(bp->name,sp->name,pass); SetScope(bp->name); if (!NewTypeContext(type)) { continue; } for (pp = sp->promiselist; pp != NULL; pp=pp->next) { SaveClassEnvironment(); ExpandPromise(cf_agent,bp->name,pp,KeepAgentPromise); if (Abort()) { NoteClassUsage(VADDCLASSES); DeleteTypeContext(type); return false; } } DeleteTypeContext(type); } } NoteClassUsage(VADDCLASSES); return true; }
void EvalContextStackFrameAddSoft(EvalContext *ctx, const char *context) { assert(SeqLength(ctx->stack) > 0); StackFrameBundle frame; { StackFrame *last_frame = LastStackFrameBundle(ctx); if (!last_frame) { ProgrammingError("Attempted to add a soft class on the stack, but stack had no bundle frame"); } frame = last_frame->data.bundle; } char copy[CF_BUFSIZE]; if (strcmp(frame.owner->ns, "default") != 0) { snprintf(copy, CF_MAXVARSIZE, "%s:%s", frame.owner->ns, context); } else { strncpy(copy, context, CF_MAXVARSIZE); } if (Chop(copy, CF_EXPANDSIZE) == -1) { CfOut(OUTPUT_LEVEL_ERROR, "", "Chop was called on a string that seemed to have no terminator"); } if (strlen(copy) == 0) { return; } CfDebug("NewBundleClass(%s)\n", copy); if (IsRegexItemIn(ctx, ctx->heap_abort_current_bundle, copy)) { CfOut(OUTPUT_LEVEL_ERROR, "", "Bundle %s aborted on defined class \"%s\"\n", frame.owner->name, copy); ABORTBUNDLE = true; } if (IsRegexItemIn(ctx, ctx->heap_abort, copy)) { CfOut(OUTPUT_LEVEL_ERROR, "", "cf-agent aborted on defined class \"%s\" defined in bundle %s\n", copy, frame.owner->name); exit(1); } if (EvalContextHeapContainsSoft(ctx, copy)) { CfOut(OUTPUT_LEVEL_ERROR, "", "WARNING - private class \"%s\" in bundle \"%s\" shadows a global class - you should choose a different name to avoid conflicts", copy, frame.owner->name); } if (EvalContextStackFrameContainsSoft(ctx, copy)) { return; } StringSetAdd(frame.contexts, xstrdup(copy)); for (const Item *ip = ctx->heap_abort; ip != NULL; ip = ip->next) { if (IsDefinedClass(ctx, ip->name, frame.owner->ns)) { CfOut(OUTPUT_LEVEL_ERROR, "", "cf-agent aborted on defined class \"%s\" defined in bundle %s\n", copy, frame.owner->name); exit(1); } } if (!ABORTBUNDLE) { for (const Item *ip = ctx->heap_abort_current_bundle; ip != NULL; ip = ip->next) { if (IsDefinedClass(ctx, ip->name, frame.owner->ns)) { CfOut(OUTPUT_LEVEL_ERROR, "", " -> Setting abort for \"%s\" when setting \"%s\"", ip->name, context); ABORTBUNDLE = true; break; } } } }
void EvalContextHeapAddSoft(EvalContext *ctx, const char *context, const char *ns) { char context_copy[CF_MAXVARSIZE]; char canonified_context[CF_MAXVARSIZE]; strcpy(canonified_context, context); if (Chop(canonified_context, CF_EXPANDSIZE) == -1) { Log(LOG_LEVEL_ERR, "Chop was called on a string that seemed to have no terminator"); } CanonifyNameInPlace(canonified_context); if (ns && strcmp(ns, "default") != 0) { snprintf(context_copy, CF_MAXVARSIZE, "%s:%s", ns, canonified_context); } else { strncpy(context_copy, canonified_context, CF_MAXVARSIZE); } if (strlen(context_copy) == 0) { return; } if (IsRegexItemIn(ctx, ctx->heap_abort_current_bundle, context_copy)) { Log(LOG_LEVEL_ERR, "Bundle aborted on defined class '%s'", context_copy); ABORTBUNDLE = true; } if (IsRegexItemIn(ctx, ctx->heap_abort, context_copy)) { Log(LOG_LEVEL_ERR, "cf-agent aborted on defined class '%s'", context_copy); exit(1); } if (EvalContextHeapContainsSoft(ctx, context_copy)) { return; } StringSetAdd(ctx->heap_soft, xstrdup(context_copy)); for (const Item *ip = ctx->heap_abort; ip != NULL; ip = ip->next) { if (IsDefinedClass(ctx, ip->name, ns)) { Log(LOG_LEVEL_ERR, "cf-agent aborted on defined class '%s' defined in bundle '%s'", ip->name, StackFrameOwnerName(LastStackFrame(ctx, 0))); exit(1); } } if (!ABORTBUNDLE) { for (const Item *ip = ctx->heap_abort_current_bundle; ip != NULL; ip = ip->next) { if (IsDefinedClass(ctx, ip->name, ns)) { Log(LOG_LEVEL_ERR, "Setting abort for '%s' when setting '%s'", ip->name, context_copy); ABORTBUNDLE = true; break; } } } }
void NewBundleClass(EvalContext *ctx, const char *context, const char *bundle, const char *ns) { char copy[CF_BUFSIZE]; if (ns && strcmp(ns, "default") != 0) { snprintf(copy, CF_MAXVARSIZE, "%s:%s", ns, context); } else { strncpy(copy, context, CF_MAXVARSIZE); } if (Chop(copy, CF_EXPANDSIZE) == -1) { CfOut(OUTPUT_LEVEL_ERROR, "", "Chop was called on a string that seemed to have no terminator"); } if (strlen(copy) == 0) { return; } CfDebug("NewBundleClass(%s)\n", copy); if (IsRegexItemIn(ctx, ctx->heap_abort_current_bundle, copy)) { CfOut(OUTPUT_LEVEL_ERROR, "", "Bundle %s aborted on defined class \"%s\"\n", bundle, copy); ABORTBUNDLE = true; } if (IsRegexItemIn(ctx, ctx->heap_abort, copy)) { CfOut(OUTPUT_LEVEL_ERROR, "", "cf-agent aborted on defined class \"%s\" defined in bundle %s\n", copy, bundle); exit(1); } if (EvalContextHeapContainsSoft(ctx, copy)) { CfOut(OUTPUT_LEVEL_ERROR, "", "WARNING - private class \"%s\" in bundle \"%s\" shadows a global class - you should choose a different name to avoid conflicts", copy, bundle); } if (EvalContextStackFrameContainsSoft(ctx, copy)) { return; } EvalContextStackFrameAddSoft(ctx, copy); for (const Item *ip = ctx->heap_abort; ip != NULL; ip = ip->next) { if (IsDefinedClass(ctx, ip->name, ns)) { CfOut(OUTPUT_LEVEL_ERROR, "", "cf-agent aborted on defined class \"%s\" defined in bundle %s\n", copy, bundle); exit(1); } } if (!ABORTBUNDLE) { for (const Item *ip = ctx->heap_abort_current_bundle; ip != NULL; ip = ip->next) { if (IsDefinedClass(ctx, ip->name, ns)) { CfOut(OUTPUT_LEVEL_ERROR, "", " -> Setting abort for \"%s\" when setting \"%s\"", ip->name, context); ABORTBUNDLE = true; break; } } } }
int ScheduleAgentOperations(Bundle *bp, const ReportContext *report_context) // NB - this function can be called recursively through "methods" { SubType *sp; enum typesequence type; int pass; int save_pr_kept = PR_KEPT; int save_pr_repaired = PR_REPAIRED; int save_pr_notkept = PR_NOTKEPT; if (PROCESSREFRESH == NULL || (PROCESSREFRESH && IsRegexItemIn(PROCESSREFRESH, bp->name))) { DeleteItemList(PROCESSTABLE); PROCESSTABLE = NULL; } for (pass = 1; pass < CF_DONEPASSES; pass++) { for (type = 0; AGENT_TYPESEQUENCE[type] != NULL; type++) { ClassBanner(type); if ((sp = BundleGetSubType(bp, AGENT_TYPESEQUENCE[type])) == NULL) { continue; } BannerSubType(bp->name, sp->name, pass); SetScope(bp->name); if (!NewTypeContext(type)) { continue; } for (size_t ppi = 0; ppi < SeqLength(sp->promises); ppi++) { Promise *pp = SeqAt(sp->promises, ppi); if (ALLCLASSESREPORT) { SaveClassEnvironment(); } if (pass == 1) // Count the number of promises modelled for efficiency { CF_TOPICS++; } ExpandPromise(AGENT_TYPE_AGENT, bp->name, pp, KeepAgentPromise, report_context); if (Abort()) { NoteClassUsage(VADDCLASSES, false); DeleteTypeContext(bp->parent_policy, type, report_context); NoteBundleCompliance(bp, save_pr_kept, save_pr_repaired, save_pr_notkept); return false; } } DeleteTypeContext(bp->parent_policy, type, report_context); } } NoteClassUsage(VADDCLASSES, false); return NoteBundleCompliance(bp, save_pr_kept, save_pr_repaired, save_pr_notkept); }
/* Checks the "varadmit" legacy ACL. */ static Item *ContextAccessControl(EvalContext *ctx, char *in, ServerConnectionState *conn, int encrypt) { Auth *ap; int access = false; char client_regex[CF_BUFSIZE]; Item *ip, *matches = NULL; int ret = sscanf(in, "CONTEXT %255[^\n]", client_regex); Item *persistent_classes = ListPersistentClasses(); if (ret != 1 || persistent_classes == NULL) { return NULL; } for (ip = persistent_classes; ip != NULL; ip = ip->next) { /* Does the class match the regex that the agent requested? */ if (StringMatchFull(client_regex, ip->name)) { for (ap = SV.varadmit; ap != NULL; ap = ap->next) { /* Does the class match any of the regex in ACLs? */ if (StringMatchFull(ap->path, ip->name)) { Log(LOG_LEVEL_VERBOSE, "Found a matching rule in access list (%s in %s)", ip->name, ap->path); if (ap->classpattern == false) { Log(LOG_LEVEL_ERR, "Context %s requires a literal server item...cannot set variable directly by path", ap->path); access = false; continue; } if ((!encrypt) && (ap->encrypt == true)) { Log(LOG_LEVEL_ERR, "Context %s requires encrypt connection...will not serve", ip->name); access = false; break; } else { Log(LOG_LEVEL_DEBUG, "Checking whether to map root privileges"); if ((IsMatchItemIn(ap->maproot, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->maproot, conn->hostname))) { conn->maproot = true; Log(LOG_LEVEL_VERBOSE, "Mapping root privileges"); } else { Log(LOG_LEVEL_VERBOSE, "No root privileges granted"); } if ((IsMatchItemIn(ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = true; Log(LOG_LEVEL_DEBUG, "Access privileges - match found"); } } } } for (ap = SV.vardeny; ap != NULL; ap = ap->next) { if (strcmp(ap->path, ip->name) == 0) { if ((IsMatchItemIn(ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = false; Log(LOG_LEVEL_VERBOSE, "Host %s explicitly denied access to context %s", conn->hostname, ip->name); break; } } } if (access) { Log(LOG_LEVEL_VERBOSE, "Host %s granted access to context '%s'", conn->hostname, ip->name); AppendItem(&matches, ip->name, NULL); if (encrypt && LOGENCRYPT) { /* Log files that were marked as requiring encryption */ Log(LOG_LEVEL_INFO, "Host %s granted access to context '%s'", conn->hostname, ip->name); } } else { Log(LOG_LEVEL_VERBOSE, "Host %s denied access to context '%s'", conn->hostname, ip->name); } } } DeleteItemList(persistent_classes); return matches; }
/* Checks the "varadmit" legacy ACL. */ static int LiteralAccessControl(EvalContext *ctx, char *in, ServerConnectionState *conn, int encrypt) { Auth *ap; int access = false; char name[CF_BUFSIZE]; name[0] = '\0'; if (strncmp(in, "VAR", 3) == 0) { sscanf(in, "VAR %255[^\n]", name); } else if (strncmp(in, "CALL_ME_BACK", strlen("CALL_ME_BACK")) == 0) { sscanf(in, "CALL_ME_BACK %255[^\n]", name); } else { sscanf(in, "QUERY %128s", name); } conn->maproot = false; for (ap = SV.varadmit; ap != NULL; ap = ap->next) { Log(LOG_LEVEL_VERBOSE, "Examining rule in access list (%s,%s)?", name, ap->path); if (strcmp(ap->path, name) == 0) /* exact match */ { Log(LOG_LEVEL_VERBOSE, "Found a matching rule in access list (%s in %s)", name, ap->path); if ((!ap->literal) && (!ap->variable)) { Log(LOG_LEVEL_ERR, "Variable/query '%s' requires a literal server item...cannot set variable directly by path", ap->path); access = false; break; } if ((!encrypt) && (ap->encrypt == true)) { Log(LOG_LEVEL_ERR, "Variable %s requires encrypt connection...will not serve", name); access = false; break; } else { Log(LOG_LEVEL_DEBUG, "Checking whether to map root privileges"); if ((IsMatchItemIn(ap->maproot, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->maproot, conn->hostname))) { conn->maproot = true; Log(LOG_LEVEL_VERBOSE, "Mapping root privileges"); } else { Log(LOG_LEVEL_VERBOSE, "No root privileges granted"); } if ((IsMatchItemIn(ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = true; Log(LOG_LEVEL_DEBUG, "Access privileges - match found"); } } } } for (ap = SV.vardeny; ap != NULL; ap = ap->next) { if (strcmp(ap->path, name) == 0) { if ((IsMatchItemIn(ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = false; Log(LOG_LEVEL_VERBOSE, "Host %s explicitly denied access to %s", conn->hostname, name); break; } } } if (access) { Log(LOG_LEVEL_VERBOSE, "Host %s granted access to literal '%s'", conn->hostname, name); if (encrypt && LOGENCRYPT) { /* Log files that were marked as requiring encryption */ Log(LOG_LEVEL_INFO, "Host %s granted access to literal '%s'", conn->hostname, name); } } else { Log(LOG_LEVEL_VERBOSE, "Host %s denied access to literal '%s'", conn->hostname, name); } return access; }
static int AccessControl(EvalContext *ctx, const char *req_path, ServerConnectionState *conn, int encrypt) { int access = false; char transrequest[CF_BUFSIZE]; struct stat statbuf; char translated_req_path[CF_BUFSIZE]; char transpath[CF_BUFSIZE]; /* * /var/cfengine -> $workdir translation. */ TranslatePath(translated_req_path, req_path); if (ResolveFilename(translated_req_path, transrequest)) { Log(LOG_LEVEL_VERBOSE, "Filename %s is resolved to %s", translated_req_path, transrequest); } else { Log(LOG_LEVEL_INFO, "Couldn't resolve (realpath: %s) filename: %s", GetErrorStr(), translated_req_path); return false; /* can't continue without transrequest */ } if (lstat(transrequest, &statbuf) == -1) { Log(LOG_LEVEL_INFO, "Couldn't stat (lstat: %s) filename: %s", GetErrorStr(), transrequest); return false; } Log(LOG_LEVEL_DEBUG, "AccessControl, match (%s,%s) encrypt request = %d", transrequest, conn->hostname, encrypt); if (SV.admit == NULL) { Log(LOG_LEVEL_INFO, "cf-serverd access list is empty, no files are visible"); return false; } conn->maproot = false; for (Auth *ap = SV.admit; ap != NULL; ap = ap->next) { int res = false; Log(LOG_LEVEL_DEBUG, "Examining rule in access list (%s,%s)", transrequest, ap->path); /* TODO MapName when constructing this list. */ strncpy(transpath, ap->path, CF_BUFSIZE - 1); MapName(transpath); /* If everything is allowed */ if ((strcmp(transpath, FILE_SEPARATOR_STR) == 0) || /* or if transpath is a parent directory of transrequest */ (strlen(transrequest) > strlen(transpath) && strncmp(transpath, transrequest, strlen(transpath)) == 0 && transrequest[strlen(transpath)] == FILE_SEPARATOR) || /* or if it's an exact match */ (strcmp(transpath, transrequest) == 0)) { res = true; } /* Exact match means single file to admit */ if (strcmp(transpath, transrequest) == 0) { res = true; } if (res) { Log(LOG_LEVEL_VERBOSE, "Found a matching rule in access list (%s in %s)", transrequest, transpath); if (stat(transpath, &statbuf) == -1) { Log(LOG_LEVEL_INFO, "Warning cannot stat file object %s in admit/grant, or access list refers to dangling link", transpath); continue; } if ((!encrypt) && (ap->encrypt == true)) { Log(LOG_LEVEL_ERR, "File %s requires encrypt connection...will not serve", transpath); access = false; } else { Log(LOG_LEVEL_DEBUG, "Checking whether to map root privileges.."); if ((IsMatchItemIn(ap->maproot, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->maproot, conn->hostname))) { conn->maproot = true; Log(LOG_LEVEL_VERBOSE, "Mapping root privileges to access non-root files"); } if ((IsMatchItemIn(ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = true; Log(LOG_LEVEL_DEBUG, "Access granted to host: %s", conn->ipaddr); } } break; } } for (Auth *dp = SV.deny; dp != NULL; dp = dp->next) { strncpy(transpath, dp->path, CF_BUFSIZE - 1); MapName(transpath); /* If everything is denied */ if ((strcmp(transpath, FILE_SEPARATOR_STR) == 0) || /* or if transpath is a parent directory of transrequest */ (strlen(transrequest) > strlen(transpath) && strncmp(transpath, transrequest, strlen(transpath)) == 0 && transrequest[strlen(transpath)] == FILE_SEPARATOR) || /* or if it's an exact match */ (strcmp(transpath, transrequest) == 0)) { if ((IsMatchItemIn(dp->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, dp->accesslist, conn->hostname))) { access = false; Log(LOG_LEVEL_INFO, "Host '%s' in deny list, explicitly denying access to '%s'", conn->ipaddr, transrequest); break; } } } if (access) { Log(LOG_LEVEL_VERBOSE, "Host %s granted access to %s", conn->hostname, req_path); if (encrypt && LOGENCRYPT) { /* Log files that were marked as requiring encryption */ Log(LOG_LEVEL_INFO, "Host %s granted access to %s", conn->hostname, req_path); } } else { Log(LOG_LEVEL_INFO, "Host %s denied access to %s", conn->hostname, req_path); } return access; }
static int AuthorizeRoles(EvalContext *ctx, ServerConnectionState *conn, char *args) { char *sp; Auth *ap; char userid1[CF_MAXVARSIZE], userid2[CF_MAXVARSIZE]; Rlist *rp, *defines = NULL; int permitted = false; snprintf(userid1, CF_MAXVARSIZE, "%s@%s", conn->username, conn->hostname); snprintf(userid2, CF_MAXVARSIZE, "%s@%s", conn->username, conn->ipaddr); Log(LOG_LEVEL_VERBOSE, "Checking authorized roles in %s", args); if (strncmp(args, "--define", strlen("--define")) == 0) { sp = args + strlen("--define"); } else { sp = args + strlen("-D"); } while (*sp == ' ') { sp++; } defines = RlistFromSplitRegex(ctx, sp, "[,:;]", 99, false); /* For each user-defined class attempt, check RBAC */ for (rp = defines; rp != NULL; rp = rp->next) { Log(LOG_LEVEL_VERBOSE, "Verifying %s", RlistScalarValue(rp)); for (ap = SV.roles; ap != NULL; ap = ap->next) { if (FullTextMatch(ctx, ap->path, RlistScalarValue(rp))) { /* We have a pattern covering this class - so are we allowed to activate it? */ if ((IsMatchItemIn(ctx, ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname)) || (IsRegexItemIn(ctx, ap->accesslist, userid1)) || (IsRegexItemIn(ctx, ap->accesslist, userid2)) || (IsRegexItemIn(ctx, ap->accesslist, conn->username))) { Log(LOG_LEVEL_VERBOSE, "Attempt to define role/class %s is permitted", RlistScalarValue(rp)); permitted = true; } else { Log(LOG_LEVEL_VERBOSE, "Attempt to define role/class %s is denied", RlistScalarValue(rp)); RlistDestroy(defines); return false; } } } } if (permitted) { Log(LOG_LEVEL_VERBOSE, "Role activation allowed"); } else { Log(LOG_LEVEL_VERBOSE, "Role activation disallowed - abort execution"); } RlistDestroy(defines); return permitted; }
Item *ContextAccessControl(EvalContext *ctx, char *in, ServerConnectionState *conn, int encrypt) { Auth *ap; int access = false; char client_regex[CF_BUFSIZE]; CF_DB *dbp; CF_DBC *dbcp; int ksize, vsize; char *key; void *value; time_t now = time(NULL); CfState q; Item *ip, *matches = NULL, *candidates = NULL; sscanf(in, "CONTEXT %255[^\n]", client_regex); if (!OpenDB(&dbp, dbid_state)) { return NULL; } if (!NewDBCursor(dbp, &dbcp)) { Log(LOG_LEVEL_INFO, "Unable to scan persistence cache"); CloseDB(dbp); return NULL; } while (NextDB(dbcp, &key, &ksize, &value, &vsize)) { memcpy((void *) &q, value, sizeof(CfState)); if (now > q.expires) { Log(LOG_LEVEL_VERBOSE, " Persistent class %s expired", key); DBCursorDeleteEntry(dbcp); } else { if (FullTextMatch(ctx, client_regex, key)) { Log(LOG_LEVEL_VERBOSE, " - Found key %s...", key); AppendItem(&candidates, key, NULL); } } } DeleteDBCursor(dbcp); CloseDB(dbp); for (ip = candidates; ip != NULL; ip = ip->next) { for (ap = SV.varadmit; ap != NULL; ap = ap->next) { int res = false; if (FullTextMatch(ctx, ap->path, ip->name)) { res = true; } if (res) { Log(LOG_LEVEL_VERBOSE, "Found a matching rule in access list (%s in %s)", ip->name, ap->path); if (ap->classpattern == false) { Log(LOG_LEVEL_ERR, "Context %s requires a literal server item...cannot set variable directly by path", ap->path); access = false; continue; } if ((!encrypt) && (ap->encrypt == true)) { Log(LOG_LEVEL_ERR, "Context %s requires encrypt connection...will not serve", ip->name); access = false; break; } else { Log(LOG_LEVEL_DEBUG, "Checking whether to map root privileges"); if ((IsMatchItemIn(ctx, ap->maproot, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->maproot, conn->hostname))) { conn->maproot = true; Log(LOG_LEVEL_VERBOSE, "Mapping root privileges"); } else { Log(LOG_LEVEL_VERBOSE, "No root privileges granted"); } if ((IsMatchItemIn(ctx, ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = true; Log(LOG_LEVEL_DEBUG, "Access privileges - match found"); } } } } for (ap = SV.vardeny; ap != NULL; ap = ap->next) { if (strcmp(ap->path, ip->name) == 0) { if ((IsMatchItemIn(ctx, ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = false; Log(LOG_LEVEL_VERBOSE, "Host %s explicitly denied access to context %s", conn->hostname, ip->name); break; } } } if (access) { Log(LOG_LEVEL_VERBOSE, "Host %s granted access to context '%s'", conn->hostname, ip->name); AppendItem(&matches, ip->name, NULL); if (encrypt && LOGENCRYPT) { /* Log files that were marked as requiring encryption */ Log(LOG_LEVEL_INFO, "Host %s granted access to context '%s'", conn->hostname, ip->name); } } else { Log(LOG_LEVEL_VERBOSE, "Host %s denied access to context '%s'", conn->hostname, ip->name); } } DeleteItemList(candidates); return matches; }
int AccessControl(EvalContext *ctx, const char *req_path, ServerConnectionState *conn, int encrypt) { Auth *ap; int access = false; char transrequest[CF_BUFSIZE]; struct stat statbuf; char translated_req_path[CF_BUFSIZE]; char transpath[CF_BUFSIZE]; /* * /var/cfengine -> $workdir translation. */ TranslatePath(translated_req_path, req_path); if (ResolveFilename(translated_req_path, transrequest)) { Log(LOG_LEVEL_VERBOSE, "Filename %s is resolved to %s", translated_req_path, transrequest); } else { Log(LOG_LEVEL_INFO, "Couldn't resolve (realpath: %s) filename: %s", GetErrorStr(), translated_req_path); } if (lstat(transrequest, &statbuf) == -1) { Log(LOG_LEVEL_INFO, "Couldn't stat (lstat: %s) filename: %s", GetErrorStr(), transrequest); return false; } Log(LOG_LEVEL_DEBUG, "AccessControl, match (%s,%s) encrypt request = %d", transrequest, conn->hostname, encrypt); if (SV.admit == NULL) { Log(LOG_LEVEL_INFO, "cf-serverd access list is empty, no files are visible"); return false; } conn->maproot = false; for (ap = SV.admit; ap != NULL; ap = ap->next) { int res = false; Log(LOG_LEVEL_DEBUG, "Examining rule in access list (%s,%s)", transrequest, ap->path); strncpy(transpath, ap->path, CF_BUFSIZE - 1); MapName(transpath); if ((strlen(transrequest) > strlen(transpath)) && (strncmp(transpath, transrequest, strlen(transpath)) == 0) && (transrequest[strlen(transpath)] == FILE_SEPARATOR)) { res = true; /* Substring means must be a / to link, else just a substring og filename */ } /* Exact match means single file to admit */ if (strcmp(transpath, transrequest) == 0) { res = true; } if (strcmp(transpath, "/") == 0) { res = true; } if (res) { Log(LOG_LEVEL_VERBOSE, "Found a matching rule in access list (%s in %s)", transrequest, transpath); if (stat(transpath, &statbuf) == -1) { Log(LOG_LEVEL_INFO, "Warning cannot stat file object %s in admit/grant, or access list refers to dangling link\n", transpath); continue; } if ((!encrypt) && (ap->encrypt == true)) { Log(LOG_LEVEL_ERR, "File %s requires encrypt connection...will not serve", transpath); access = false; } else { Log(LOG_LEVEL_DEBUG, "Checking whether to map root privileges.."); if ((IsMatchItemIn(ctx, ap->maproot, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->maproot, conn->hostname))) { conn->maproot = true; Log(LOG_LEVEL_VERBOSE, "Mapping root privileges to access non-root files"); } if ((IsMatchItemIn(ctx, ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = true; Log(LOG_LEVEL_DEBUG, "Access privileges - match found"); } } break; } } if (strncmp(transpath, transrequest, strlen(transpath)) == 0) { for (ap = SV.deny; ap != NULL; ap = ap->next) { if (IsRegexItemIn(ctx, ap->accesslist, conn->hostname)) { access = false; Log(LOG_LEVEL_INFO, "Host %s explicitly denied access to %s", conn->hostname, transrequest); break; } } } if (access) { Log(LOG_LEVEL_VERBOSE, "Host %s granted access to %s", conn->hostname, req_path); if (encrypt && LOGENCRYPT) { /* Log files that were marked as requiring encryption */ Log(LOG_LEVEL_INFO, "Host %s granted access to %s", conn->hostname, req_path); } } else { Log(LOG_LEVEL_INFO, "Host %s denied access to %s", conn->hostname, req_path); } if (!conn->rsa_auth) { Log(LOG_LEVEL_INFO, "Cannot map root access without RSA authentication"); conn->maproot = false; /* only public files accessible */ } return access; }