static int UserSanityCheck(Attributes a, Promise *pp) { User *u = &a.users; switch (u->policy) { case USER_STATE_PRESENT: case USER_STATE_ABSENT: case USER_STATE_LOCKED: break; default: Log(LOG_LEVEL_ERR, "No policy specified for 'users' promise '%s'", pp->promiser); PromiseRef(LOG_LEVEL_ERR, pp); return false; } if ((SafeStringLength(u->password) == 0 && u->password_format != PASSWORD_FORMAT_NONE) || (SafeStringLength(u->password) != 0 && u->password_format == PASSWORD_FORMAT_NONE)) { Log(LOG_LEVEL_ERR, "Both 'data' and 'format' must be specified in password body for 'users' promise '%s'", pp->promiser); PromiseRef(LOG_LEVEL_ERR, pp); return false; } return true; }
bool CFTestD_GetServerQuery( ServerConnectionState *conn, char *recvbuffer, CFTestD_Config *config) { char query[CF_BUFSIZE]; Seq *report = config->report; const int report_len = config->report_len; ConnectionInfo *const conn_info = conn->conn_info; query[0] = '\0'; sscanf(recvbuffer, "QUERY %255[^\n]", query); if (strlen(query) == 0) { return false; } if (report_len == 0) { Log(LOG_LEVEL_INFO, "No report file argument so returning canned data from enterprise plugin server\n"); return CFTestD_ReturnQueryData(conn, query); } Log(LOG_LEVEL_INFO, "Report file argument specified. Returning report of length %d", report_len); const size_t n_report_lines = SeqLength(report); assert(n_report_lines > 1); char *ts = SeqAt(report, 0); char *header = StringFormat("CFR: 0 %s %d\n", ts, report_len); SendTransaction(conn_info, header, SafeStringLength(header), CF_MORE); free(header); for (size_t i = 1; i < n_report_lines; i++) { const char *report_line = SeqAt(report, i); SendTransaction(conn_info, report_line, SafeStringLength(report_line), CF_MORE); } const char end_reply[] = "QUERY complete"; SendTransaction(conn_info, end_reply, SafeStringLength(end_reply), CF_DONE); return true; }
void ReplaceTrailingChar(char *str, char from, char to) /* Replaces any unwanted last char in str. */ { int strLen; strLen = SafeStringLength(str); if (strLen == 0) { return; } if (str[strLen - 1] == from) { str[strLen - 1] = to; } }
static bool VerifyIfUserNeedsModifs (const char *puser, const User *u, const struct passwd *passwd_info, uint32_t *changemap) { assert(u != NULL); if (u->description != NULL && strcmp (u->description, passwd_info->pw_gecos)) { CFUSR_SETBIT (*changemap, i_comment); } if (u->uid != NULL && (atoi (u->uid) != passwd_info->pw_uid)) { CFUSR_SETBIT (*changemap, i_uid); } if (u->home_dir != NULL && strcmp (u->home_dir, passwd_info->pw_dir)) { CFUSR_SETBIT (*changemap, i_home); } if (u->shell != NULL && strcmp (u->shell, passwd_info->pw_shell)) { CFUSR_SETBIT (*changemap, i_shell); } bool account_is_locked = IsAccountLocked(puser, passwd_info); if ((!account_is_locked && u->policy == USER_STATE_LOCKED) || (account_is_locked && u->policy != USER_STATE_LOCKED)) { CFUSR_SETBIT(*changemap, i_locked); } // Don't bother with passwords if the account is going to be locked anyway. if (u->password != NULL && strcmp (u->password, "") && u->policy != USER_STATE_LOCKED) { if (!IsPasswordCorrect(puser, u->password, u->password_format, passwd_info)) { CFUSR_SETBIT (*changemap, i_password); } } if (SafeStringLength(u->group_primary)) { bool group_could_be_gid = (strlen(u->group_primary) == strspn(u->group_primary, "0123456789")); int gid; // We try name first, even if it looks like a gid. Only fall back to gid. struct group *group_info; errno = 0; group_info = GetGrEntry(u->group_primary, &EqualGroupName); if (!group_info && errno != 0) { Log(LOG_LEVEL_ERR, "Could not obtain information about group '%s': %s", u->group_primary, GetErrorStr()); gid = -1; } else if (!group_info) { if (group_could_be_gid) { gid = atoi(u->group_primary); } else { Log(LOG_LEVEL_ERR, "No such group '%s'.", u->group_primary); gid = -1; } } else { gid = group_info->gr_gid; } if (gid != passwd_info->pw_gid) { CFUSR_SETBIT (*changemap, i_group); } } if (u->groups_secondary_given) { StringSet *wanted_groups = StringSetNew(); for (Rlist *ptr = u->groups_secondary; ptr; ptr = ptr->next) { StringSetAdd(wanted_groups, xstrdup(RvalScalarValue(ptr->val))); } TransformGidsToGroups(&wanted_groups); StringSet *current_groups = StringSetNew(); if (!GroupGetUserMembership (puser, current_groups)) { CFUSR_SETBIT (*changemap, i_groups); } else if (!StringSetIsEqual (current_groups, wanted_groups)) { CFUSR_SETBIT (*changemap, i_groups); } StringSetDestroy(current_groups); StringSetDestroy(wanted_groups); } //////////////////////////////////////////// if (*changemap == 0) { return false; } else { return true; } }
static void MailResult(const ExecConfig *config, const char *file) { #if defined __linux__ || defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__ time_t now = time(NULL); #endif Log(LOG_LEVEL_VERBOSE, "Mail result..."); { struct stat statbuf; if (stat(file, &statbuf) == -1) { return; } if (statbuf.st_size == 0) { unlink(file); Log(LOG_LEVEL_DEBUG, "Nothing to report in file '%s'", file); return; } } { char prev_file[CF_BUFSIZE]; snprintf(prev_file, CF_BUFSIZE - 1, "%s/outputs/previous", CFWORKDIR); MapName(prev_file); if (CompareResult(file, prev_file) == 0) { Log(LOG_LEVEL_VERBOSE, "Previous output is the same as current so do not mail it"); return; } } if ((strlen(config->mail_server) == 0) || (strlen(config->mail_to_address) == 0)) { /* Syslog should have done this */ Log(LOG_LEVEL_VERBOSE, "Empty mail server or address - skipping"); return; } if (config->mail_max_lines == 0) { Log(LOG_LEVEL_DEBUG, "Not mailing: EmailMaxLines was zero"); return; } Log(LOG_LEVEL_DEBUG, "Mailing results of '%s' to '%s'", file, config->mail_to_address); /* Check first for anomalies - for subject header */ FILE *fp = fopen(file, "r"); if (!fp) { Log(LOG_LEVEL_INFO, "Couldn't open file '%s'. (fopen: %s)", file, GetErrorStr()); return; } bool anomaly = false; char vbuff[CF_BUFSIZE]; while (!feof(fp)) { vbuff[0] = '\0'; if (fgets(vbuff, sizeof(vbuff), fp) == NULL) { break; } if (strstr(vbuff, "entropy")) { anomaly = true; break; } } fclose(fp); if ((fp = fopen(file, "r")) == NULL) { Log(LOG_LEVEL_INFO, "Couldn't open file '%s'. (fopen: %s)", file, GetErrorStr()); return; } struct hostent *hp = gethostbyname(config->mail_server); if (!hp) { Log(LOG_LEVEL_ERR, "While mailing agent output, unknown host '%s'. Make sure that fully qualified names can be looked up at your site.", config->mail_server); fclose(fp); return; } struct servent *server = getservbyname("smtp", "tcp"); if (!server) { Log(LOG_LEVEL_INFO, "Unable to lookup smtp service. (getservbyname: %s)", GetErrorStr()); fclose(fp); return; } struct sockaddr_in raddr; memset(&raddr, 0, sizeof(raddr)); raddr.sin_port = (unsigned int) server->s_port; raddr.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr))->s_addr; raddr.sin_family = AF_INET; Log(LOG_LEVEL_DEBUG, "Connecting..."); int sd = socket(AF_INET, SOCK_STREAM, 0); if (sd == -1) { Log(LOG_LEVEL_INFO, "Couldn't open a socket. (socket: %s)", GetErrorStr()); fclose(fp); return; } if (connect(sd, (void *) &raddr, sizeof(raddr)) == -1) { Log(LOG_LEVEL_INFO, "Couldn't connect to host '%s'. (connect: %s)", config->mail_server, GetErrorStr()); fclose(fp); cf_closesocket(sd); return; } /* read greeting */ if (!Dialogue(sd, NULL)) { goto mail_err; } sprintf(vbuff, "HELO %s\r\n", config->fq_name); Log(LOG_LEVEL_DEBUG, "%s", vbuff); if (!Dialogue(sd, vbuff)) { goto mail_err; } if (strlen(config->mail_from_address) == 0) { sprintf(vbuff, "MAIL FROM: <cfengine@%s>\r\n", config->fq_name); Log(LOG_LEVEL_DEBUG, "%s", vbuff); } else { sprintf(vbuff, "MAIL FROM: <%s>\r\n", config->mail_from_address); Log(LOG_LEVEL_DEBUG, "%s", vbuff); } if (!Dialogue(sd, vbuff)) { goto mail_err; } sprintf(vbuff, "RCPT TO: <%s>\r\n", config->mail_to_address); Log(LOG_LEVEL_DEBUG, "%s", vbuff); if (!Dialogue(sd, vbuff)) { goto mail_err; } if (!Dialogue(sd, "DATA\r\n")) { goto mail_err; } char mailsubject_anomaly_prefix[8]; if (anomaly) { strcpy(mailsubject_anomaly_prefix, "**!! "); } else { mailsubject_anomaly_prefix[0] = '\0'; } if (SafeStringLength(config->mail_subject) == 0) { snprintf(vbuff, sizeof(vbuff), "Subject: %s[%s/%s]\r\n", mailsubject_anomaly_prefix, config->fq_name, config->ip_address); Log(LOG_LEVEL_DEBUG, "%s", vbuff); } else { snprintf(vbuff, sizeof(vbuff), "Subject: %s%s\r\n", mailsubject_anomaly_prefix, config->mail_subject); Log(LOG_LEVEL_DEBUG, "%s", vbuff); } send(sd, vbuff, strlen(vbuff), 0); /* send X-CFEngine SMTP header if mailsubject set */ if (SafeStringLength(config->mail_subject) > 0) { unsigned char digest[EVP_MAX_MD_SIZE + 1]; char buffer[EVP_MAX_MD_SIZE * 4]; char *existing_policy_server = ReadPolicyServerFile(GetWorkDir()); HashPubKey(PUBKEY, digest, CF_DEFAULT_DIGEST); snprintf(vbuff, sizeof(vbuff), "X-CFEngine: vfqhost=\"%s\";ip-addresses=\"%s\";policyhub=\"%s\";pkhash=\"%s\"\r\n", VFQNAME, config->ip_addresses, existing_policy_server, HashPrintSafe(CF_DEFAULT_DIGEST, true, digest, buffer)); send(sd, vbuff, strlen(vbuff), 0); free(existing_policy_server); } #if defined __linux__ || defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__ strftime(vbuff, CF_BUFSIZE, "Date: %a, %d %b %Y %H:%M:%S %z\r\n", localtime(&now)); send(sd, vbuff, strlen(vbuff), 0); #endif if (strlen(config->mail_from_address) == 0) { sprintf(vbuff, "From: cfengine@%s\r\n", config->fq_name); Log(LOG_LEVEL_DEBUG, "%s", vbuff); } else { sprintf(vbuff, "From: %s\r\n", config->mail_from_address); Log(LOG_LEVEL_DEBUG, "%s", vbuff); } send(sd, vbuff, strlen(vbuff), 0); sprintf(vbuff, "To: %s\r\n\r\n", config->mail_to_address); Log(LOG_LEVEL_DEBUG, "%s", vbuff); send(sd, vbuff, strlen(vbuff), 0); int count = 0; while (!feof(fp)) { vbuff[0] = '\0'; if (fgets(vbuff, sizeof(vbuff), fp) == NULL) { break; } Log(LOG_LEVEL_DEBUG, "%s", vbuff); if (strlen(vbuff) > 0) { vbuff[strlen(vbuff) - 1] = '\r'; strcat(vbuff, "\n"); count++; send(sd, vbuff, strlen(vbuff), 0); } if ((config->mail_max_lines != INF_LINES) && (count > config->mail_max_lines)) { sprintf(vbuff, "\r\n[Mail truncated by cfengine. File is at %s on %s]\r\n", file, config->fq_name); send(sd, vbuff, strlen(vbuff), 0); break; } } if (!Dialogue(sd, ".\r\n")) { Log(LOG_LEVEL_DEBUG, "mail_err\n"); goto mail_err; } Dialogue(sd, "QUIT\r\n"); Log(LOG_LEVEL_DEBUG, "Done sending mail"); fclose(fp); cf_closesocket(sd); return; mail_err: fclose(fp); cf_closesocket(sd); Log(LOG_LEVEL_INFO, "Cannot mail to %s.", config->mail_to_address); }
void ExecConfigUpdate(const EvalContext *ctx, const Policy *policy, ExecConfig *exec_config) { ExecConfigResetDefault(exec_config); Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_EXECUTOR); if (constraints) { for (size_t i = 0; i < SeqLength(constraints); i++) { Constraint *cp = SeqAt(constraints, i); if (!IsDefinedClass(ctx, cp->classes, NULL)) { continue; } VarRef *ref = VarRefParseFromScope(cp->lval, "control_executor"); Rval retval; if (!EvalContextVariableGet(ctx, ref, &retval, NULL)) { // TODO: should've been checked before this point. change to programming error Log(LOG_LEVEL_ERR, "Unknown lval '%s' in exec control body", cp->lval); VarRefDestroy(ref); continue; } VarRefDestroy(ref); if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILFROM].lval) == 0) { free(exec_config->mail_from_address); exec_config->mail_from_address = xstrdup(retval.item); Log(LOG_LEVEL_DEBUG, "mailfrom '%s'", exec_config->mail_from_address); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILTO].lval) == 0) { free(exec_config->mail_to_address); exec_config->mail_to_address = xstrdup(retval.item); Log(LOG_LEVEL_DEBUG, "mailto '%s'", exec_config->mail_to_address); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILSUBJECT].lval) == 0) { free(exec_config->mail_subject); exec_config->mail_subject = xstrdup(retval.item); Log(LOG_LEVEL_DEBUG, "mailsubject '%s'", exec_config->mail_subject); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_SMTPSERVER].lval) == 0) { free(exec_config->mail_server); exec_config->mail_server = xstrdup(retval.item); Log(LOG_LEVEL_DEBUG, "smtpserver '%s'", exec_config->mail_server); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_EXECCOMMAND].lval) == 0) { free(exec_config->exec_command); exec_config->exec_command = xstrdup(retval.item); Log(LOG_LEVEL_DEBUG, "exec_command '%s'", exec_config->exec_command); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_AGENT_EXPIREAFTER].lval) == 0) { exec_config->agent_expireafter = IntFromString(retval.item); Log(LOG_LEVEL_DEBUG, "agent_expireafter %d", exec_config->agent_expireafter); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_EXECUTORFACILITY].lval) == 0) { exec_config->log_facility = xstrdup(retval.item); Log(LOG_LEVEL_DEBUG, "executorfacility '%s'", exec_config->log_facility); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILMAXLINES].lval) == 0) { exec_config->mail_max_lines = IntFromString(retval.item); Log(LOG_LEVEL_DEBUG, "maxlines %d", exec_config->mail_max_lines); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_SPLAYTIME].lval) == 0) { int time = IntFromString(RvalScalarValue(retval)); exec_config->splay_time = (int) (time * SECONDS_PER_MINUTE * GetSplay()); } else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_SCHEDULE].lval) == 0) { Log(LOG_LEVEL_DEBUG, "Loading user-defined schedule..."); StringSetClear(exec_config->schedule); for (const Rlist *rp = retval.item; rp; rp = rp->next) { StringSetAdd(exec_config->schedule, xstrdup(RlistScalarValue(rp))); Log(LOG_LEVEL_DEBUG, "Adding '%s'", RlistScalarValue(rp)); } } } } char ipbuf[CF_MAXVARSIZE] = ""; for (Item *iptr = EvalContextGetIpAddresses(ctx); iptr != NULL; iptr = iptr->next) { if ((SafeStringLength(ipbuf) + SafeStringLength(iptr->name)) < sizeof(ipbuf)) { strcat(ipbuf, iptr->name); strcat(ipbuf, " "); } else { break; } } Chop(ipbuf, sizeof(ipbuf)); free(exec_config->ip_addresses); exec_config->ip_addresses = xstrdup(ipbuf); }
static void MailResult(const ExecConfig *config, const char *file) { size_t line_size = CF_BUFSIZE; char *line = xmalloc(line_size); ssize_t read; #if defined __linux__ || defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__ time_t now = time(NULL); #endif Log(LOG_LEVEL_VERBOSE, "Mail report: sending result..."); { struct stat statbuf; if (stat(file, &statbuf) == -1) { return; } if (statbuf.st_size == 0) { unlink(file); Log(LOG_LEVEL_DEBUG, "Mail report: nothing to report in file '%s'", file); return; } } { char prev_file[CF_BUFSIZE]; snprintf(prev_file, CF_BUFSIZE, "%s/outputs/previous", GetWorkDir()); MapName(prev_file); if (CompareResultEqualOrFiltered(config, file, prev_file)) { Log(LOG_LEVEL_VERBOSE, "Mail report: previous output is the same as current so do not mail it"); return; } } if ((strlen(config->mail_server) == 0) || (strlen(config->mail_to_address) == 0)) { /* Syslog should have done this */ Log(LOG_LEVEL_VERBOSE, "Mail report: empty mail server or address - skipping"); return; } if (config->mail_max_lines == 0) { Log(LOG_LEVEL_DEBUG, "Mail report: not mailing because EmailMaxLines was zero"); return; } Log(LOG_LEVEL_DEBUG, "Mail report: mailing results of '%s' to '%s'", file, config->mail_to_address); FILE *fp = fopen(file, "r"); if (fp == NULL) { Log(LOG_LEVEL_ERR, "Mail report: couldn't open file '%s'. (fopen: %s)", file, GetErrorStr()); return; } int sd = ConnectToSmtpSocket(config); if (sd < 0) { fclose(fp); return; } /* read greeting */ if (!Dialogue(sd, NULL)) { goto mail_err; } char vbuff[CF_BUFSIZE]; snprintf(vbuff, sizeof(vbuff), "HELO %s\r\n", config->fq_name); Log(LOG_LEVEL_DEBUG, "Mail report: %s", vbuff); if (!Dialogue(sd, vbuff)) { goto mail_err; } if (strlen(config->mail_from_address) == 0) { snprintf(vbuff, sizeof(vbuff), "MAIL FROM: <cfengine@%s>\r\n", config->fq_name); Log(LOG_LEVEL_DEBUG, "Mail report: %s", vbuff); } else { snprintf(vbuff, sizeof(vbuff), "MAIL FROM: <%s>\r\n", config->mail_from_address); Log(LOG_LEVEL_DEBUG, "Mail report: %s", vbuff); } if (!Dialogue(sd, vbuff)) { goto mail_err; } snprintf(vbuff, sizeof(vbuff), "RCPT TO: <%s>\r\n", config->mail_to_address); Log(LOG_LEVEL_DEBUG, "Mail report: %s", vbuff); if (!Dialogue(sd, vbuff)) { goto mail_err; } if (!Dialogue(sd, "DATA\r\n")) { goto mail_err; } if (SafeStringLength(config->mail_subject) == 0) { snprintf(vbuff, sizeof(vbuff), "Subject: [%s/%s]\r\n", config->fq_name, config->ip_address); Log(LOG_LEVEL_DEBUG, "Mail report: %s", vbuff); } else { snprintf(vbuff, sizeof(vbuff), "Subject: %s\r\n", config->mail_subject); Log(LOG_LEVEL_DEBUG, "Mail report: %s", vbuff); } send(sd, vbuff, strlen(vbuff), 0); /* Send X-CFEngine SMTP header */ unsigned char digest[EVP_MAX_MD_SIZE + 1]; char buffer[CF_HOSTKEY_STRING_SIZE]; char *existing_policy_server = ReadPolicyServerFile(GetWorkDir()); if (!existing_policy_server) { existing_policy_server = xstrdup("(none)"); } HashPubKey(PUBKEY, digest, CF_DEFAULT_DIGEST); snprintf(vbuff, sizeof(vbuff), "X-CFEngine: vfqhost=\"%s\";ip-addresses=\"%s\";policyhub=\"%s\";pkhash=\"%s\"\r\n", VFQNAME, config->ip_addresses, existing_policy_server, HashPrintSafe(buffer, sizeof(buffer), digest, CF_DEFAULT_DIGEST, true)); send(sd, vbuff, strlen(vbuff), 0); free(existing_policy_server); #if defined __linux__ || defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__ strftime(vbuff, CF_BUFSIZE, "Date: %a, %d %b %Y %H:%M:%S %z\r\n", localtime(&now)); send(sd, vbuff, strlen(vbuff), 0); #endif if (strlen(config->mail_from_address) == 0) { snprintf(vbuff, sizeof(vbuff), "From: cfengine@%s\r\n", config->fq_name); Log(LOG_LEVEL_DEBUG, "Mail report: %s", vbuff); } else { snprintf(vbuff, sizeof(vbuff), "From: %s\r\n", config->mail_from_address); Log(LOG_LEVEL_DEBUG, "Mail report: %s", vbuff); } send(sd, vbuff, strlen(vbuff), 0); snprintf(vbuff, sizeof(vbuff), "To: %s\r\n\r\n", config->mail_to_address); Log(LOG_LEVEL_DEBUG, "Mail report: %s", vbuff); send(sd, vbuff, strlen(vbuff), 0); int count = 0; while ((read = CfReadLine(&line, &line_size, fp)) > 0) { if (LineIsFiltered(config, line)) { continue; } if (send(sd, line, read, 0) == -1 || send(sd, "\r\n", 2, 0) == -1) { Log(LOG_LEVEL_ERR, "Error while sending mail to mailserver " "'%s'. (send: '%s')", config->mail_server, GetErrorStr()); goto mail_err; } count++; if ((config->mail_max_lines != INF_LINES) && (count >= config->mail_max_lines)) { snprintf(line, line_size, "\r\n[Mail truncated by CFEngine. File is at %s on %s]\r\n", file, config->fq_name); if (send(sd, line, strlen(line), 0)) { Log(LOG_LEVEL_ERR, "Error while sending mail to mailserver " "'%s'. (send: '%s')", config->mail_server, GetErrorStr()); goto mail_err; } break; } } if (!Dialogue(sd, ".\r\n")) { Log(LOG_LEVEL_DEBUG, "Mail report: mail_err\n"); goto mail_err; } Dialogue(sd, "QUIT\r\n"); Log(LOG_LEVEL_DEBUG, "Mail report: done sending mail"); free(line); fclose(fp); cf_closesocket(sd); return; mail_err: free(line); fclose(fp); cf_closesocket(sd); Log(LOG_LEVEL_ERR, "Mail report: cannot mail to %s.", config->mail_to_address); }