int ConnectToSmtpSocket(ARG_UNUSED const ExecConfig *config) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) { return -1; } int *thread_socket = xmalloc(sizeof(int)); *thread_socket = sockets[1]; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_t thread; int ret = pthread_create(&thread, &attr, &FeedSmtpDirectives, thread_socket); pthread_attr_destroy(&attr); if (ret != 0) { free(thread_socket); cf_closesocket(sockets[0]); cf_closesocket(sockets[1]); return -1; } return sockets[0]; }
static void CollectCallIfDue(EvalContext *ctx) { /* Check whether we have established peering with a hub */ if (CollectCallHasPending()) { extern int COLLECT_WINDOW; int waiting_queue = 0; int new_client = CollectCallGetPending(&waiting_queue); assert(new_client >= 0); if (waiting_queue > COLLECT_WINDOW) { Log(LOG_LEVEL_INFO, "Abandoning collect call attempt with queue longer than collect_window [%d > %d]", waiting_queue, COLLECT_WINDOW); cf_closesocket(new_client); CollectCallMarkProcessed(); } else { ConnectionInfo *info = ConnectionInfoNew(); assert(info); ConnectionInfoSetSocket(info, new_client); info->is_call_collect = true; /* Mark processed when done. */ ServerEntryPoint(ctx, PolicyServerGetIP(), info); } } }
static void *FeedSmtpDirectives(void *data) { int socket = *(int *)data; #define COND_SEND(X) if (send(socket, X, strlen(X), 0) == -1) \ { \ Log(LOG_LEVEL_ERR, "Failed to write SMTP directive in %s:%i", __FILE__, __LINE__); \ return NULL; \ } \ else \ { \ fwrite(X, strlen(X), 1, stdout); \ } #define COND_RECV(X) if ((rcvd = recv(socket, X, sizeof(X), 0)) == -1) \ { \ Log(LOG_LEVEL_ERR, "Failed to read SMTP response in %s:%i", __FILE__, __LINE__); \ return NULL; \ } \ else \ { \ fwrite(X, rcvd, 1, stdout); \ } char recvbuf[CF_BUFSIZE]; int rcvd; COND_SEND("220 test.com\r\n"); COND_RECV(recvbuf); COND_SEND("250 Hello test.com, pleased to meet you\r\n"); COND_RECV(recvbuf); COND_SEND("250 [email protected]... Sender ok\r\n"); COND_RECV(recvbuf); COND_SEND("250 [email protected]... Recipient ok\r\n"); COND_RECV(recvbuf); COND_SEND("354 Enter mail, end with \".\" on a line by itself\r\n"); while (true) { COND_RECV(recvbuf); if ((rcvd == 3 && memcmp(recvbuf + rcvd - 3, ".\r\n", 3) == 0) || (rcvd > 3 && memcmp(recvbuf + rcvd - 4, "\n.\r\n", 4) == 0)) { break; } } COND_SEND("250 Message accepted for delivery\r\n"); COND_RECV(recvbuf); COND_SEND("221 test.com closing connection\r\n"); #undef COND_SEND #undef COND_RECV cf_closesocket(socket); free(data); return NULL; }
static void CFTestD_DeleteConn(ServerConnectionState *conn) { int sd = ConnectionInfoSocket(conn->conn_info); if (sd != SOCKET_INVALID) { cf_closesocket(sd); } ConnectionInfoDestroy(&conn->conn_info); free(conn->session_key); free(conn); }
void DisconnectServer(AgentConnection *conn) { if (conn) { if (conn->sd >= 0) /* Not INVALID or OFFLINE */ { cf_closesocket(conn->sd); conn->sd = SOCKET_INVALID; } DeleteAgentConn(conn); } }
void DisconnectServer(AgentConnection *conn) { /* Socket needs to be closed even after SSL_shutdown. */ if (conn->conn_info->sd >= 0) /* Not INVALID or OFFLINE */ { if (conn->conn_info->protocol >= CF_PROTOCOL_TLS && conn->conn_info->ssl != NULL) { SSL_shutdown(conn->conn_info->ssl); } cf_closesocket(conn->conn_info->sd); conn->conn_info->sd = SOCKET_INVALID; Log(LOG_LEVEL_VERBOSE, "Connection to %s is closed", conn->remoteip); } DeleteAgentConn(conn); }
/** * @note This function is thread-safe. Do NOT wrap it with mutex! */ static void DeleteConn(ServerConnectionState *conn) { cf_closesocket(ConnectionInfoSocket(conn->conn_info)); ConnectionInfoDestroy(&conn->conn_info); free(conn->session_key); if (conn->ipaddr != NULL) { if (ThreadLock(cft_count)) { DeleteItemMatching(&SV.connectionlist, conn->ipaddr); ThreadUnlock(cft_count); } } *conn = (ServerConnectionState) {0}; free(conn); }
int ConnectToSmtpSocket(const ExecConfig *config) { struct hostent *hp = gethostbyname(config->mail_server); if (!hp) { Log(LOG_LEVEL_ERR, "Mail report: unknown host '%s' ('smtpserver' in body executor control). Make sure that fully qualified names can be looked up at your site.", config->mail_server); return -1; } struct servent *server = getservbyname("smtp", "tcp"); if (!server) { Log(LOG_LEVEL_ERR, "Mail report: unable to lookup smtp service. (getservbyname: %s)", GetErrorStr()); return -1; } 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, "Mail report: connecting..."); int sd = socket(AF_INET, SOCK_STREAM, 0); if (sd == -1) { Log(LOG_LEVEL_ERR, "Mail report: couldn't open a socket. (socket: %s)", GetErrorStr()); return -1; } if (connect(sd, (void *) &raddr, sizeof(raddr)) == -1) { Log(LOG_LEVEL_ERR, "Mail report: couldn't connect to host '%s'. (connect: %s)", config->mail_server, GetErrorStr()); cf_closesocket(sd); return -1; } return sd; }
int CFTestD_StartServer(CFTestD_Config *config) { int ret = -1; bool tls_init_ok = ServerTLSInitialize(config->priv_key, config->pub_key, &(config->ssl_ctx)); if (!tls_init_ok) { return -1; } int sd = InitServer(CFTESTD_QUEUE_SIZE, config->address); int selected = 0; while (!TERMINATE && (selected != -1)) { selected = WaitForIncoming(sd, WAIT_CHECK_TIMEOUT); if (selected > 0) { Log(LOG_LEVEL_DEBUG, "select(): %d", selected); CFTestD_AcceptAndHandle(sd, config); } } if (!TERMINATE) { Log(LOG_LEVEL_ERR, "Error while waiting for connections. (select: %s)", GetErrorStr()); } else { ret = 0; } Log(LOG_LEVEL_NOTICE, "Cleaning up and exiting..."); if (sd != -1) { Log(LOG_LEVEL_VERBOSE, "Closing listening socket"); cf_closesocket(sd); } return ret; }
static void MailResult(const ExecConfig *config, char *file) { int sd, count = 0, anomaly = false; char prev_file[CF_BUFSIZE], vbuff[CF_BUFSIZE]; struct hostent *hp; struct sockaddr_in raddr; struct servent *server; struct stat statbuf; #if defined __linux__ || defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__ time_t now = time(NULL); #endif FILE *fp; CfOut(cf_verbose, "", "Mail result...\n"); if (cfstat(file, &statbuf) == -1) { return; } snprintf(prev_file, CF_BUFSIZE - 1, "%s/outputs/previous", CFWORKDIR); MapName(prev_file); if (statbuf.st_size == 0) { unlink(file); CfDebug("Nothing to report in %s\n", file); return; } if (CompareResult(file, prev_file) == 0) { CfOut(cf_verbose, "", "Previous output is the same as current so do not mail it\n"); return; } if ((strlen(config->mail_server) == 0) || (strlen(config->mail_to_address) == 0)) { /* Syslog should have done this */ CfOut(cf_verbose, "", "Empty mail server or address - skipping"); return; } if (config->mail_max_lines == 0) { CfDebug("Not mailing: EmailMaxLines was zero\n"); return; } CfDebug("Mailing results of (%s) to (%s)\n", file, config->mail_to_address); /* Check first for anomalies - for subject header */ if ((fp = fopen(file, "r")) == NULL) { CfOut(cf_inform, "fopen", "!! Couldn't open file %s", file); return; } while (!feof(fp)) { vbuff[0] = '\0'; if (fgets(vbuff, CF_BUFSIZE, fp) == NULL) { break; } if (strstr(vbuff, "entropy")) { anomaly = true; break; } } fclose(fp); if ((fp = fopen(file, "r")) == NULL) { CfOut(cf_inform, "fopen", "Couldn't open file %s", file); return; } CfDebug("Looking up hostname %s\n\n", config->mail_server); if ((hp = gethostbyname(config->mail_server)) == NULL) { printf("Unknown host: %s\n", config->mail_server); printf("Make sure that fully qualified names can be looked up at your site.\n"); fclose(fp); return; } if ((server = getservbyname("smtp", "tcp")) == NULL) { CfOut(cf_inform, "getservbyname", "Unable to lookup smtp service"); fclose(fp); return; } 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; CfDebug("Connecting...\n"); if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { CfOut(cf_inform, "socket", "Couldn't open a socket"); fclose(fp); return; } if (connect(sd, (void *) &raddr, sizeof(raddr)) == -1) { CfOut(cf_inform, "connect", "Couldn't connect to host %s\n", config->mail_server); fclose(fp); cf_closesocket(sd); return; } /* read greeting */ if (!Dialogue(sd, NULL)) { goto mail_err; } sprintf(vbuff, "HELO %s\r\n", config->fq_name); CfDebug("%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); CfDebug("%s", vbuff); } else { sprintf(vbuff, "MAIL FROM: <%s>\r\n", config->mail_from_address); CfDebug("%s", vbuff); } if (!Dialogue(sd, vbuff)) { goto mail_err; } sprintf(vbuff, "RCPT TO: <%s>\r\n", config->mail_to_address); CfDebug("%s", vbuff); if (!Dialogue(sd, vbuff)) { goto mail_err; } if (!Dialogue(sd, "DATA\r\n")) { goto mail_err; } if (anomaly) { sprintf(vbuff, "Subject: %s **!! [%s/%s]\r\n", MailSubject(), config->fq_name, config->ip_address); CfDebug("%s", vbuff); } else { sprintf(vbuff, "Subject: %s [%s/%s]\r\n", MailSubject(), config->fq_name, config->ip_address); CfDebug("%s", vbuff); } send(sd, vbuff, strlen(vbuff), 0); #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); CfDebug("%s", vbuff); } else { sprintf(vbuff, "From: %s\r\n", config->mail_from_address); CfDebug("%s", vbuff); } send(sd, vbuff, strlen(vbuff), 0); sprintf(vbuff, "To: %s\r\n\r\n", config->mail_to_address); CfDebug("%s", vbuff); send(sd, vbuff, strlen(vbuff), 0); while (!feof(fp)) { vbuff[0] = '\0'; if (fgets(vbuff, CF_BUFSIZE, fp) == NULL) { break; } CfDebug("%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")) { CfDebug("mail_err\n"); goto mail_err; } Dialogue(sd, "QUIT\r\n"); CfDebug("Done sending mail\n"); fclose(fp); cf_closesocket(sd); return; mail_err: fclose(fp); cf_closesocket(sd); CfOut(cf_log, "", "Cannot mail to %s.", config->mail_to_address); }
static int OpenReceiverChannel(void) { struct addrinfo *response = NULL, *ap; struct addrinfo query = { .ai_flags = AI_PASSIVE, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM }; /* Listen to INADDR(6)_ANY if BINDINTERFACE unset. */ char *ptr = NULL; if (BINDINTERFACE[0] != '\0') { ptr = BINDINTERFACE; } char servname[PRINTSIZE(CFENGINE_PORT)]; xsnprintf(servname, sizeof(servname), "%d", CFENGINE_PORT); /* Resolve listening interface. */ int gres; if ((gres = getaddrinfo(ptr, servname, &query, &response)) != 0) { Log(LOG_LEVEL_ERR, "DNS/service lookup failure. (getaddrinfo: %s)", gai_strerror(gres)); if (response) { freeaddrinfo(response); } return -1; } int sd = -1; for (ap = response; ap != NULL; ap = ap->ai_next) { sd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol); if (sd == -1) { continue; } #ifdef IPV6_V6ONLY /* Properly implemented getaddrinfo(AI_PASSIVE) should return the IPV6 loopback address first. Some platforms (notably Windows) don't listen to both address families when binding to it and need this flag. Some other platforms won't even honour this flag (openbsd). */ if (BINDINTERFACE[0] == '\0' && ap->ai_family == AF_INET6) { int no = 0; if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no)) == -1) { Log(LOG_LEVEL_VERBOSE, "Failed to clear IPv6-only flag on listening socket" " (setsockopt: %s)", GetErrorStr()); } } #endif int yes = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { Log(LOG_LEVEL_VERBOSE, "Socket option SO_REUSEADDR was not accepted. (setsockopt: %s)", GetErrorStr()); } struct linger cflinger = { .l_onoff = 1, .l_linger = 60 }; if (setsockopt(sd, SOL_SOCKET, SO_LINGER, &cflinger, sizeof(cflinger)) == -1) { Log(LOG_LEVEL_INFO, "Socket option SO_LINGER was not accepted. (setsockopt: %s)", GetErrorStr()); } if (bind(sd, ap->ai_addr, ap->ai_addrlen) != -1) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { /* Convert IP address to string, no DNS lookup performed. */ char txtaddr[CF_MAX_IP_LEN] = ""; getnameinfo(ap->ai_addr, ap->ai_addrlen, txtaddr, sizeof(txtaddr), NULL, 0, NI_NUMERICHOST); Log(LOG_LEVEL_DEBUG, "Bound to address '%s' on '%s' = %d", txtaddr, CLASSTEXT[VSYSTEMHARDCLASS], VSYSTEMHARDCLASS); } break; } Log(LOG_LEVEL_INFO, "Could not bind server address. (bind: %s)", GetErrorStr()); cf_closesocket(sd); sd = -1; } assert(response != NULL); /* getaddrinfo() was successful */ freeaddrinfo(response); return sd; } static int InitServer(size_t queue_size) { int sd = OpenReceiverChannel(); if (sd == -1) { Log(LOG_LEVEL_ERR, "Unable to start server"); } else if (listen(sd, queue_size) == -1) { Log(LOG_LEVEL_ERR, "listen failed. (listen: %s)", GetErrorStr()); cf_closesocket(sd); } else { return sd; } exit(EXIT_FAILURE); }
void ServerEntryPoint(EvalContext *ctx, char *ipaddr, ConnectionInfo *info) { char intime[64]; time_t now; Log(LOG_LEVEL_VERBOSE, "Obtained IP address of '%s' on socket %d from accept", ipaddr, ConnectionInfoSocket(info)); if ((SV.nonattackerlist) && (!IsMatchItemIn(SV.nonattackerlist, MapAddress(ipaddr)))) { Log(LOG_LEVEL_ERR, "Not allowing connection from non-authorized IP '%s'", ipaddr); cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } if (IsMatchItemIn(SV.attackerlist, MapAddress(ipaddr))) { Log(LOG_LEVEL_ERR, "Denying connection from non-authorized IP '%s'", ipaddr); cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } if ((now = time((time_t *) NULL)) == -1) { now = 0; } PurgeOldConnections(&SV.connectionlist, now); if (!IsMatchItemIn(SV.multiconnlist, MapAddress(ipaddr))) { if (!ThreadLock(cft_count)) { return; } if (IsItemIn(SV.connectionlist, MapAddress(ipaddr))) { ThreadUnlock(cft_count); Log(LOG_LEVEL_ERR, "Denying repeated connection from '%s'", ipaddr); cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } ThreadUnlock(cft_count); } if (SV.logconns) { Log(LOG_LEVEL_INFO, "Accepting connection from %s", ipaddr); } else { Log(LOG_LEVEL_INFO, "Accepting connection from %s", ipaddr); } snprintf(intime, 63, "%d", (int) now); if (!ThreadLock(cft_count)) { cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } PrependItem(&SV.connectionlist, MapAddress(ipaddr), intime); if (!ThreadUnlock(cft_count)) { cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } SpawnConnection(ctx, ipaddr, info); }
static void *HandleConnection(ServerConnectionState *conn) { int ret; char output[CF_BUFSIZE]; /* Set logging prefix to be the IP address for all of thread's lifetime. */ /* Should be valid for all lifetime of the thread. Just make sure that * after calling DeleteConn(), you exit right away. */ LoggingPrivContext log_ctx = { .log_hook = LogHook, .param = conn->ipaddr }; LoggingPrivSetContext(&log_ctx); if (!ThreadLock(cft_server_children)) { DeleteConn(conn); return NULL; } ACTIVE_THREADS++; if (ACTIVE_THREADS >= CFD_MAXPROCESSES) { ACTIVE_THREADS--; if (TRIES++ > MAXTRIES) /* When to say we're hung / apoptosis threshold */ { Log(LOG_LEVEL_ERR, "Server seems to be paralyzed. DOS attack? Committing apoptosis..."); FatalError(conn->ctx, "Terminating"); } if (!ThreadUnlock(cft_server_children)) { } Log(LOG_LEVEL_ERR, "Too many threads (>=%d) -- increase server maxconnections?", CFD_MAXPROCESSES); snprintf(output, CF_BUFSIZE, "BAD: Server is currently too busy -- increase maxconnections or splaytime?"); SendTransaction(conn->conn_info, output, 0, CF_DONE); DeleteConn(conn); return NULL; } else { ThreadUnlock(cft_server_children); } TRIES = 0; /* As long as there is activity, we're not stuck */ DisableSendDelays(ConnectionInfoSocket(conn->conn_info)); struct timeval tv = { .tv_sec = CONNTIMEOUT * 20, }; SetReceiveTimeout(ConnectionInfoSocket(conn->conn_info), &tv); if (ConnectionInfoConnectionStatus(conn->conn_info) != CF_CONNECTION_ESTABLISHED) { /* Decide the protocol used. */ ret = ServerTLSPeek(conn->conn_info); if (ret == -1) { DeleteConn(conn); return NULL; } } switch (ConnectionInfoProtocolVersion(conn->conn_info)) { case CF_PROTOCOL_CLASSIC: { while (BusyWithClassicConnection(conn->ctx, conn)) { } break; } case CF_PROTOCOL_TLS: { ret = ServerTLSSessionEstablish(conn); if (ret == -1) { DeleteConn(conn); return NULL; } while (BusyWithNewProtocol(conn->ctx, conn)) { } break; } default: UnexpectedError("HandleConnection: ProtocolVersion %d!", ConnectionInfoProtocolVersion(conn->conn_info)); } Log(LOG_LEVEL_INFO, "Connection closed, terminating thread", conn->ipaddr); if (!ThreadLock(cft_server_children)) { DeleteConn(conn); return NULL; } ACTIVE_THREADS--; if (!ThreadUnlock(cft_server_children)) { } DeleteConn(conn); return NULL; } /***************************************************************/ /* Toolkit/Class: conn */ /***************************************************************/ static ServerConnectionState *NewConn(EvalContext *ctx, ConnectionInfo *info) { ServerConnectionState *conn = NULL; struct sockaddr_storage addr; socklen_t size = sizeof(addr); if (getsockname(ConnectionInfoSocket(info), (struct sockaddr *)&addr, &size) == -1) { Log(LOG_LEVEL_ERR, "Could not obtain socket address. (getsockname: '%s')", GetErrorStr()); return NULL; } conn = xcalloc(1, sizeof(*conn)); conn->ctx = ctx; conn->conn_info = info; conn->id_verified = false; conn->rsa_auth = false; conn->hostname[0] = '\0'; conn->ipaddr[0] = '\0'; conn->username[0] = '\0'; conn->session_key = NULL; conn->encryption_type = 'c'; conn->maproot = false; /* Only public files (chmod o+r) accessible */ Log(LOG_LEVEL_DEBUG, "New socket %d", ConnectionInfoSocket(info)); return conn; } /***************************************************************/ static void DeleteConn(ServerConnectionState *conn) { cf_closesocket(ConnectionInfoSocket(conn->conn_info)); ConnectionInfoDestroy(&conn->conn_info); free(conn->session_key); if (conn->ipaddr != NULL) { if (!ThreadLock(cft_count)) { return; } DeleteItemMatching(&SV.connectionlist, MapAddress(conn->ipaddr)); if (!ThreadUnlock(cft_count)) { return; } } *conn = (ServerConnectionState) {0}; free(conn); }
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); }
int OpenReceiverChannel(void) { int sd; int yes = 1; struct linger cflinger; #if defined(HAVE_GETADDRINFO) struct addrinfo query, *response, *ap; #else struct sockaddr_in sin; #endif cflinger.l_onoff = 1; cflinger.l_linger = 60; #if defined(HAVE_GETADDRINFO) char *ptr = NULL; memset(&query, 0, sizeof(struct addrinfo)); query.ai_flags = AI_PASSIVE; query.ai_family = AF_UNSPEC; query.ai_socktype = SOCK_STREAM; /* * HvB : Bas van der Vlies */ if (BINDINTERFACE[0] != '\0') { ptr = BINDINTERFACE; } if (getaddrinfo(ptr, STR_CFENGINEPORT, &query, &response) != 0) { CfOut(cf_error, "getaddrinfo", "DNS/service lookup failure"); return -1; } sd = -1; for (ap = response; ap != NULL; ap = ap->ai_next) { if ((sd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol)) == -1) { continue; } if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(int)) == -1) { CfOut(cf_error, "setsockopt", "Socket options were not accepted"); exit(1); } if (setsockopt(sd, SOL_SOCKET, SO_LINGER, (char *) &cflinger, sizeof(struct linger)) == -1) { CfOut(cf_error, "setsockopt", "Socket options were not accepted"); exit(1); } if (bind(sd, ap->ai_addr, ap->ai_addrlen) == 0) { if (DEBUG) { ThreadLock(cft_getaddr); printf("Bound to address %s on %s=%d\n", sockaddr_ntop(ap->ai_addr), CLASSTEXT[VSYSTEMHARDCLASS], VSYSTEMHARDCLASS); ThreadUnlock(cft_getaddr); } #if defined(__MINGW32__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) continue; /* *bsd doesn't map ipv6 addresses */ #else break; #endif } CfOut(cf_error, "bind", "Could not bind server address"); cf_closesocket(sd); sd = -1; } if (sd < 0) { CfOut(cf_error, "", "Couldn't open bind an open socket\n"); exit(1); } if (response != NULL) { freeaddrinfo(response); } #else memset(&sin, 0, sizeof(sin)); if (BINDINTERFACE[0] != '\0') { sin.sin_addr.s_addr = GetInetAddr(BINDINTERFACE); } else { sin.sin_addr.s_addr = INADDR_ANY; } sin.sin_port = (unsigned short) SHORT_CFENGINEPORT; sin.sin_family = AF_INET; if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { CfOut(cf_error, "socket", "Couldn't open socket"); exit(1); } if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(int)) == -1) { CfOut(cf_error, "sockopt", "Couldn't set socket options"); exit(1); } if (setsockopt(sd, SOL_SOCKET, SO_LINGER, (char *) &cflinger, sizeof(struct linger)) == -1) { CfOut(cf_error, "sockopt", "Couldn't set socket options"); exit(1); } if (bind(sd, (struct sockaddr *) &sin, sizeof(sin)) == -1) { CfOut(cf_error, "bind", "Couldn't bind to socket"); exit(1); } #endif return sd; }
void ServerEntryPoint(EvalContext *ctx, const char *ipaddr, ConnectionInfo *info) { time_t now; Log(LOG_LEVEL_VERBOSE, "Obtained IP address of '%s' on socket %d from accept", ipaddr, ConnectionInfoSocket(info)); /* TODO change nonattackerlist, attackerlist and especially connectionlist * to binary searched lists, or remove them from the main thread! */ if ((SV.nonattackerlist) && (!IsMatchItemIn(SV.nonattackerlist, ipaddr))) { Log(LOG_LEVEL_ERR, "Remote host '%s' not in allowconnects, denying connection", ipaddr); cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } if (IsMatchItemIn(SV.attackerlist, ipaddr)) { Log(LOG_LEVEL_ERR, "Remote host '%s' is in denyconnects, denying connection", ipaddr); cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } if ((now = time(NULL)) == -1) { now = 0; } PurgeOldConnections(&SV.connectionlist, now); if (!IsMatchItemIn(SV.multiconnlist, ipaddr)) { if (!ThreadLock(cft_count)) { cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } if (IsItemIn(SV.connectionlist, ipaddr)) { ThreadUnlock(cft_count); Log(LOG_LEVEL_ERR, "Remote host '%s' is not in allowallconnects, denying second simultaneous connection", ipaddr); cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } ThreadUnlock(cft_count); } char intime[PRINTSIZE(now)]; snprintf(intime, sizeof(intime), "%jd", (intmax_t) now); if (!ThreadLock(cft_count)) { cf_closesocket(ConnectionInfoSocket(info)); ConnectionInfoDestroy(&info); return; } PrependItem(&SV.connectionlist, ipaddr, intime); ThreadUnlock(cft_count); SpawnConnection(ctx, ipaddr, info); }
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); }
/** Tries to connect() to server #host, returns the socket descriptor and the IP address that succeeded in #txtaddr. @param #connect_timeout how long to wait for connect(), zero blocks forever @param #txtaddr If connected successfully return the IP connected in textual representation @return Connected socket descriptor or -1 in case of failure. */ int SocketConnect(const char *host, const char *port, unsigned int connect_timeout, bool force_ipv4, char *txtaddr, size_t txtaddr_size) { struct addrinfo *response = NULL, *ap; bool connected = false; int sd = -1; struct addrinfo query = { .ai_family = force_ipv4 ? AF_INET : AF_UNSPEC, .ai_socktype = SOCK_STREAM }; int ret = getaddrinfo(host, port, &query, &response); if (ret != 0) { Log(LOG_LEVEL_INFO, "Unable to find host '%s' service '%s' (%s)", host, port, gai_strerror(ret)); if (response != NULL) { freeaddrinfo(response); } return -1; } for (ap = response; !connected && ap != NULL; ap = ap->ai_next) { /* Convert address to string. */ getnameinfo(ap->ai_addr, ap->ai_addrlen, txtaddr, txtaddr_size, NULL, 0, NI_NUMERICHOST); Log(LOG_LEVEL_VERBOSE, "Connecting to host %s, port %s as address %s", host, port, txtaddr); sd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol); if (sd == -1) { Log(LOG_LEVEL_ERR, "Couldn't open a socket to '%s' (socket: %s)", txtaddr, GetErrorStr()); } else { /* Bind socket to specific interface, if requested. */ if (BINDINTERFACE[0] != '\0') { struct addrinfo query2 = { .ai_family = force_ipv4 ? AF_INET : AF_UNSPEC, .ai_socktype = SOCK_STREAM, /* returned address is for bind() */ .ai_flags = AI_PASSIVE }; struct addrinfo *response2 = NULL, *ap2; int ret2 = getaddrinfo(BINDINTERFACE, NULL, &query2, &response2); if (ret2 != 0) { Log(LOG_LEVEL_ERR, "Unable to lookup interface '%s' to bind. (getaddrinfo: %s)", BINDINTERFACE, gai_strerror(ret2)); if (response2 != NULL) { freeaddrinfo(response2); } assert(response); /* first getaddrinfo was successful */ freeaddrinfo(response); cf_closesocket(sd); return -1; } for (ap2 = response2; ap2 != NULL; ap2 = ap2->ai_next) { if (bind(sd, ap2->ai_addr, ap2->ai_addrlen) == 0) { break; } } if (ap2 == NULL) { Log(LOG_LEVEL_ERR, "Unable to bind to interface '%s'. (bind: %s)", BINDINTERFACE, GetErrorStr()); } assert(response2); /* second getaddrinfo was successful */ freeaddrinfo(response2); } connected = TryConnect(sd, connect_timeout * 1000, ap->ai_addr, ap->ai_addrlen); if (!connected) { Log(LOG_LEVEL_VERBOSE, "Unable to connect to address %s (%s)", txtaddr, GetErrorStr()); cf_closesocket(sd); sd = -1; } } } assert(response != NULL); /* first getaddrinfo was successful */ freeaddrinfo(response); if (connected) { Log(LOG_LEVEL_VERBOSE, "Connected to host %s address %s port %s", host, txtaddr, port); } else { Log(LOG_LEVEL_VERBOSE, "Unable to connect to host %s port %s", host, port); } return sd; } #if !defined(__MINGW32__) #if defined(__hpux) && defined(__GNUC__) #pragma GCC diagnostic ignored "-Wstrict-aliasing" // HP-UX GCC type-pun warning on FD_SET() macro: // While the "fd_set" type is defined in /usr/include/sys/_fd_macros.h as a // struct of an array of "long" values in accordance with the XPG4 standard's // requirements, the macros for the FD operations "pretend it is an array of // int32_t's so the binary layout is the same for both Narrow and Wide // processes," as described in _fd_macros.h. In the FD_SET, FD_CLR, and // FD_ISSET macros at line 101, the result is cast to an "__fd_mask *" type, // which is defined as int32_t at _fd_macros.h:82. // // This conflict between the "long fds_bits[]" array in the XPG4-compliant // fd_set structure, and the cast to an int32_t - not long - pointer in the // macros, causes a type-pun warning if -Wstrict-aliasing is enabled. // The warning is merely a side effect of HP-UX working as designed, // so it can be ignored. #endif /** * Tries to connect for #timeout_ms milliseconds. On success sets the recv() * timeout to #timeout_ms as well. * * @param #timeout_ms How long to wait for connect(), if zero wait forever. * @return true on success, false otherwise. **/ bool TryConnect(int sd, unsigned long timeout_ms, const struct sockaddr *sa, socklen_t sa_len) { assert(sa != NULL); if (sd >= FD_SETSIZE) { Log(LOG_LEVEL_ERR, "Open connections exceed FD_SETSIZE limit of %d", FD_SETSIZE); return false; } /* set non-blocking socket */ int arg = fcntl(sd, F_GETFL, NULL); int ret = fcntl(sd, F_SETFL, arg | O_NONBLOCK); if (ret == -1) { Log(LOG_LEVEL_ERR, "Failed to set socket to non-blocking mode (fcntl: %s)", GetErrorStr()); } ret = connect(sd, sa, sa_len); if (ret == -1) { if (errno != EINPROGRESS) { Log(LOG_LEVEL_INFO, "Failed to connect to server (connect: %s)", GetErrorStr()); return false; } int errcode; socklen_t opt_len = sizeof(errcode); fd_set myset; FD_ZERO(&myset); FD_SET(sd, &myset); Log(LOG_LEVEL_VERBOSE, "Waiting to connect..."); struct timeval tv, *tvp; if (timeout_ms > 0) { tv.tv_sec = timeout_ms / 1000; tv.tv_usec = (timeout_ms % 1000) * 1000; tvp = &tv; } else { tvp = NULL; /* wait indefinitely */ } ret = select(sd + 1, NULL, &myset, NULL, tvp); if (ret == 0) { Log(LOG_LEVEL_INFO, "Timeout connecting to server"); return false; } if (ret == -1) { if (errno == EINTR) { Log(LOG_LEVEL_ERR, "Socket connect was interrupted by signal"); } else { Log(LOG_LEVEL_ERR, "Failure while connecting (select: %s)", GetErrorStr()); } return false; } ret = getsockopt(sd, SOL_SOCKET, SO_ERROR, (void *) &errcode, &opt_len); if (ret == -1) { Log(LOG_LEVEL_ERR, "Could not check connection status (getsockopt: %s)", GetErrorStr()); return false; } if (errcode != 0) { Log(LOG_LEVEL_INFO, "Failed to connect to server: %s", GetErrorStrFromCode(errcode)); return false; } } /* Connection succeeded, return to blocking mode. */ ret = fcntl(sd, F_SETFL, arg); if (ret == -1) { Log(LOG_LEVEL_ERR, "Failed to set socket back to blocking mode (fcntl: %s)", GetErrorStr()); } if (timeout_ms > 0) { SetReceiveTimeout(sd, timeout_ms); } return true; } #if defined(__hpux) && defined(__GNUC__) #pragma GCC diagnostic warning "-Wstrict-aliasing" #endif #endif /* !defined(__MINGW32__) */ /** * Set timeout for recv(), in milliseconds. * @param ms must be > 0. */ int SetReceiveTimeout(int fd, unsigned long ms) { assert(ms > 0); Log(LOG_LEVEL_VERBOSE, "Setting socket timeout to %lu seconds.", ms/1000); /* On windows SO_RCVTIMEO is set by a DWORD indicating the timeout in * milliseconds, on UNIX it's a struct timeval. */ #if !defined(__MINGW32__) struct timeval tv = { .tv_sec = ms / 1000, .tv_usec = (ms % 1000) * 1000 }; int ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); #else int ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &ms, sizeof(ms)); #endif if (ret != 0) { Log(LOG_LEVEL_INFO, "Failed to set socket timeout to %lu milliseconds.", ms); return -1; } return 0; }
/** * @retval >0 Number of threads still working * @retval 0 All threads are done * @retval -1 Server didn't run */ int StartServer(EvalContext *ctx, Policy **policy, GenericAgentConfig *config) { InitSignals(); ServerTLSInitialize(); int sd = SetServerListenState(ctx, QUEUESIZE, SERVER_LISTEN, &InitServer); /* Necessary for our use of select() to work in WaitForIncoming(): */ assert(sd < sizeof(fd_set) * CHAR_BIT && GetSignalPipe() < sizeof(fd_set) * CHAR_BIT); Policy *server_cfengine_policy = PolicyNew(); CfLock thislock = AcquireServerLock(ctx, config, server_cfengine_policy); if (thislock.lock == NULL) { PolicyDestroy(server_cfengine_policy); if (sd >= 0) { cf_closesocket(sd); } return -1; } PrepareServer(sd); CollectCallStart(COLLECT_INTERVAL); while (!IsPendingTermination()) { CollectCallIfDue(ctx); int selected = WaitForIncoming(sd); Log(LOG_LEVEL_DEBUG, "select(): %d", selected); if (selected == -1) { Log(LOG_LEVEL_ERR, "Error while waiting for connections. (select: %s)", GetErrorStr()); break; } else if (selected >= 0) /* timeout or success */ { PolicyUpdateIfSafe(ctx, policy, config); /* Is there a new connection pending at our listening socket? */ if (selected > 0) { AcceptAndHandle(ctx, sd); } } /* else: interrupted, maybe pending termination. */ } Log(LOG_LEVEL_NOTICE, "Cleaning up and exiting..."); CollectCallStop(); if (sd != -1) { Log(LOG_LEVEL_VERBOSE, "Closing listening socket"); cf_closesocket(sd); /* Close listening socket */ } /* This is a graceful exit, give 2 seconds chance to threads. */ int threads_left = WaitOnThreads(); YieldCurrentLock(thislock); PolicyDestroy(server_cfengine_policy); return threads_left; }
int OpenReceiverChannel(void) { struct addrinfo *response, *ap; struct addrinfo query = { .ai_flags = AI_PASSIVE, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM }; /* Listen to INADDR(6)_ANY if BINDINTERFACE unset. */ char *ptr = NULL; if (BINDINTERFACE[0] != '\0') { ptr = BINDINTERFACE; } /* Resolve listening interface. */ if (getaddrinfo(ptr, STR_CFENGINEPORT, &query, &response) != 0) { Log(LOG_LEVEL_ERR, "DNS/service lookup failure. (getaddrinfo: %s)", GetErrorStr()); return -1; } int sd = -1; for (ap = response; ap != NULL; ap = ap->ai_next) { if ((sd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol)) == -1) { continue; } int yes = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { Log(LOG_LEVEL_ERR, "Socket option SO_REUSEADDR was not accepted. (setsockopt: %s)", GetErrorStr()); exit(1); } struct linger cflinger = { .l_onoff = 1, .l_linger = 60 }; if (setsockopt(sd, SOL_SOCKET, SO_LINGER, &cflinger, sizeof(cflinger)) == -1) { Log(LOG_LEVEL_ERR, "Socket option SO_LINGER was not accepted. (setsockopt: %s)", GetErrorStr()); exit(1); } if (bind(sd, ap->ai_addr, ap->ai_addrlen) != -1) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { /* Convert IP address to string, no DNS lookup performed. */ char txtaddr[CF_MAX_IP_LEN] = ""; getnameinfo(ap->ai_addr, ap->ai_addrlen, txtaddr, sizeof(txtaddr), NULL, 0, NI_NUMERICHOST); Log(LOG_LEVEL_DEBUG, "Bound to address '%s' on '%s' = %d", txtaddr, CLASSTEXT[VSYSTEMHARDCLASS], VSYSTEMHARDCLASS); } break; } else { Log(LOG_LEVEL_ERR, "Could not bind server address. (bind: %s)", GetErrorStr()); cf_closesocket(sd); } } if (sd < 0) { Log(LOG_LEVEL_ERR, "Couldn't open/bind a socket"); exit(1); } freeaddrinfo(response); return sd; } /*********************************************************************/ /* Level 3 */ /*********************************************************************/ void CheckFileChanges(EvalContext *ctx, Policy **policy, GenericAgentConfig *config) { Log(LOG_LEVEL_DEBUG, "Checking file updates for input file '%s'", config->input_file); if (NewPromiseProposals(ctx, config, InputFiles(ctx, *policy))) { Log(LOG_LEVEL_VERBOSE, "New promises detected..."); if (CheckPromises(config)) { Log(LOG_LEVEL_INFO, "Rereading policy file '%s'", config->input_file); /* Free & reload -- lock this to avoid access errors during reload */ EvalContextHeapClear(ctx); DeleteItemList(IPADDRESSES); IPADDRESSES = NULL; DeleteItemList(SV.trustkeylist); DeleteItemList(SV.skipverify); DeleteItemList(SV.attackerlist); DeleteItemList(SV.nonattackerlist); DeleteItemList(SV.multiconnlist); DeleteAuthList(SV.admit); DeleteAuthList(SV.deny); DeleteAuthList(SV.varadmit); DeleteAuthList(SV.vardeny); DeleteAuthList(SV.roles); //DeleteRlist(VINPUTLIST); This is just a pointer, cannot free it ScopeDeleteAll(); strcpy(VDOMAIN, "undefined.domain"); POLICY_SERVER[0] = '\0'; SV.admit = NULL; SV.admittop = NULL; SV.varadmit = NULL; SV.varadmittop = NULL; SV.deny = NULL; SV.denytop = NULL; SV.vardeny = NULL; SV.vardenytop = NULL; SV.roles = NULL; SV.rolestop = NULL; SV.trustkeylist = NULL; SV.skipverify = NULL; SV.attackerlist = NULL; SV.nonattackerlist = NULL; SV.multiconnlist = NULL; PolicyDestroy(*policy); *policy = NULL; { char *existing_policy_server = ReadPolicyServerFile(GetWorkDir()); SetPolicyServer(ctx, existing_policy_server); free(existing_policy_server); } GetNameInfo3(ctx, AGENT_TYPE_SERVER); GetInterfacesInfo(ctx, AGENT_TYPE_SERVER); Get3Environment(ctx, AGENT_TYPE_SERVER); BuiltinClasses(ctx); OSClasses(ctx); KeepHardClasses(ctx); EvalContextHeapAddHard(ctx, CF_AGENTTYPES[config->agent_type]); SetReferenceTime(ctx, true); *policy = GenericAgentLoadPolicy(ctx, config); KeepPromises(ctx, *policy, config); Summarize(); } else { Log(LOG_LEVEL_INFO, "File changes contain errors -- ignoring"); PROMISETIME = time(NULL); } } else { Log(LOG_LEVEL_DEBUG, "No new promises found"); } }
int OpenReceiverChannel(void) { struct addrinfo *response, *ap; struct addrinfo query = { .ai_flags = AI_PASSIVE, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM }; /* Listen to INADDR(6)_ANY if BINDINTERFACE unset. */ char *ptr = NULL; if (BINDINTERFACE[0] != '\0') { ptr = BINDINTERFACE; } char servname[10]; snprintf(servname, 10, "%d", CFENGINE_PORT); /* Resolve listening interface. */ if (getaddrinfo(ptr, servname, &query, &response) != 0) { Log(LOG_LEVEL_ERR, "DNS/service lookup failure. (getaddrinfo: %s)", GetErrorStr()); return -1; } int sd = -1; for (ap = response; ap != NULL; ap = ap->ai_next) { if ((sd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol)) == -1) { continue; } #ifdef IPV6_V6ONLY /* Some platforms don't listen to both address families (windows) for the IPv6 loopback address and need this flag. Some other platforms won't even honour this flag (openbsd). */ if (BINDINTERFACE[0] == '\0') { int no = 0; if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no)) == -1) { Log(LOG_LEVEL_VERBOSE, "Failed to clear IPv6-only flag on listening socket" " (setsockopt: %s)", GetErrorStr()); } } #endif int yes = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { Log(LOG_LEVEL_WARNING, "Socket option SO_REUSEADDR was not accepted. (setsockopt: %s)", GetErrorStr()); } struct linger cflinger = { .l_onoff = 1, .l_linger = 60 }; if (setsockopt(sd, SOL_SOCKET, SO_LINGER, &cflinger, sizeof(cflinger)) == -1) { Log(LOG_LEVEL_ERR, "Socket option SO_LINGER was not accepted. (setsockopt: %s)", GetErrorStr()); exit(EXIT_FAILURE); } if (bind(sd, ap->ai_addr, ap->ai_addrlen) != -1) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { /* Convert IP address to string, no DNS lookup performed. */ char txtaddr[CF_MAX_IP_LEN] = ""; getnameinfo(ap->ai_addr, ap->ai_addrlen, txtaddr, sizeof(txtaddr), NULL, 0, NI_NUMERICHOST); Log(LOG_LEVEL_DEBUG, "Bound to address '%s' on '%s' = %d", txtaddr, CLASSTEXT[VSYSTEMHARDCLASS], VSYSTEMHARDCLASS); } break; } else { Log(LOG_LEVEL_ERR, "Could not bind server address. (bind: %s)", GetErrorStr()); cf_closesocket(sd); } } if (sd < 0) { Log(LOG_LEVEL_ERR, "Couldn't open/bind a socket"); exit(EXIT_FAILURE); } freeaddrinfo(response); return sd; } /*********************************************************************/ /* Level 3 */ /*********************************************************************/ static void DeleteAuthList(Auth **list, Auth **list_tail) { Auth *ap = *list; while (ap != NULL) { Auth *ap_next = ap->next; DeleteItemList(ap->accesslist); DeleteItemList(ap->maproot); free(ap->path); free(ap); /* Just make sure the tail was consistent. */ if (ap_next == NULL) assert(ap == *list_tail); ap = ap_next; } *list = NULL; *list_tail = NULL; }
int OpenReceiverChannel(void) { struct addrinfo *response, *ap; struct addrinfo query = { .ai_flags = AI_PASSIVE, .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM }; /* Listen to INADDR(6)_ANY if BINDINTERFACE unset. */ char *ptr = NULL; if (BINDINTERFACE[0] != '\0') { ptr = BINDINTERFACE; } char servname[10]; snprintf(servname, 10, "%d", CFENGINE_PORT); /* Resolve listening interface. */ if (getaddrinfo(ptr, servname, &query, &response) != 0) { Log(LOG_LEVEL_ERR, "DNS/service lookup failure. (getaddrinfo: %s)", GetErrorStr()); return -1; } int sd = -1; for (ap = response; ap != NULL; ap = ap->ai_next) { if ((sd = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol)) == -1) { continue; } int yes = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { Log(LOG_LEVEL_ERR, "Socket option SO_REUSEADDR was not accepted. (setsockopt: %s)", GetErrorStr()); exit(1); } struct linger cflinger = { .l_onoff = 1, .l_linger = 60 }; if (setsockopt(sd, SOL_SOCKET, SO_LINGER, &cflinger, sizeof(cflinger)) == -1) { Log(LOG_LEVEL_ERR, "Socket option SO_LINGER was not accepted. (setsockopt: %s)", GetErrorStr()); exit(1); } if (bind(sd, ap->ai_addr, ap->ai_addrlen) != -1) { if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG) { /* Convert IP address to string, no DNS lookup performed. */ char txtaddr[CF_MAX_IP_LEN] = ""; getnameinfo(ap->ai_addr, ap->ai_addrlen, txtaddr, sizeof(txtaddr), NULL, 0, NI_NUMERICHOST); Log(LOG_LEVEL_DEBUG, "Bound to address '%s' on '%s' = %d", txtaddr, CLASSTEXT[VSYSTEMHARDCLASS], VSYSTEMHARDCLASS); } break; } else { Log(LOG_LEVEL_ERR, "Could not bind server address. (bind: %s)", GetErrorStr()); cf_closesocket(sd); } } if (sd < 0) { Log(LOG_LEVEL_ERR, "Couldn't open/bind a socket"); exit(1); } freeaddrinfo(response); return sd; } /*********************************************************************/ /* Level 3 */ /*********************************************************************/ void CheckFileChanges(EvalContext *ctx, Policy **policy, GenericAgentConfig *config, time_t *last_policy_reload) { time_t validated_at; Log(LOG_LEVEL_DEBUG, "Checking file updates for input file '%s'", config->input_file); validated_at = ReadTimestampFromPolicyValidatedMasterfiles(config); if (*last_policy_reload < validated_at) { *last_policy_reload = validated_at; Log(LOG_LEVEL_VERBOSE, "New promises detected..."); if (GenericAgentArePromisesValid(config)) { Log(LOG_LEVEL_INFO, "Rereading policy file '%s'", config->input_file); /* Free & reload -- lock this to avoid access errors during reload */ EvalContextClear(ctx); free(SV.allowciphers); SV.allowciphers = NULL; DeleteItemList(SV.trustkeylist); DeleteItemList(SV.attackerlist); DeleteItemList(SV.nonattackerlist); DeleteItemList(SV.multiconnlist); DeleteAuthList(SV.admit); DeleteAuthList(SV.deny); DeleteAuthList(SV.varadmit); DeleteAuthList(SV.vardeny); DeleteAuthList(SV.roles); strcpy(VDOMAIN, "undefined.domain"); SV.admit = NULL; SV.admittop = NULL; SV.varadmit = NULL; SV.varadmittop = NULL; SV.deny = NULL; SV.denytop = NULL; SV.vardeny = NULL; SV.vardenytop = NULL; SV.roles = NULL; SV.rolestop = NULL; SV.trustkeylist = NULL; SV.attackerlist = NULL; SV.nonattackerlist = NULL; SV.multiconnlist = NULL; PolicyDestroy(*policy); *policy = NULL; { char *existing_policy_server = ReadPolicyServerFile(GetWorkDir()); SetPolicyServer(ctx, existing_policy_server); free(existing_policy_server); } UpdateLastPolicyUpdateTime(ctx); DetectEnvironment(ctx); KeepHardClasses(ctx); EvalContextClassPutHard(ctx, CF_AGENTTYPES[config->agent_type], "cfe_internal,source=agent"); time_t t = SetReferenceTime(); UpdateTimeClasses(ctx, t); *policy = GenericAgentLoadPolicy(ctx, config); KeepPromises(ctx, *policy, config); Summarize(); } else { Log(LOG_LEVEL_INFO, "File changes contain errors -- ignoring"); } } else { Log(LOG_LEVEL_DEBUG, "No new promises found"); } }