bool Address2Hostkey(const char *address, char *result) { result[0] = '\0'; if ((strcmp(address, "127.0.0.1") == 0) || (strcmp(address, "::1") == 0) || (strcmp(address, VIPADDRESS) == 0)) { if (PUBKEY) { unsigned char digest[EVP_MAX_MD_SIZE + 1]; HashPubKey(PUBKEY, digest, CF_DEFAULT_DIGEST); HashPrintSafe(CF_DEFAULT_DIGEST, digest, result); return true; } else { return false; } } DBHandle *db; if (!OpenDB(&db, dbid_lastseen)) { return false; } bool ret = Address2HostkeyInDB(db, address, result); CloseDB(db); return ret; }
/** Return a string with the printed digest of the given key file, or NULL if an error occurred. */ char* GetPubkeyDigest(const char* pubkey) { unsigned char digest[EVP_MAX_MD_SIZE + 1]; RSA* key = NULL; char* buffer = xmalloc(EVP_MAX_MD_SIZE * 4); key = LoadPublicKey(pubkey); if (NULL == key) { return NULL; } HashPubKey(key, digest, CF_DEFAULT_DIGEST); HashPrintSafe(CF_DEFAULT_DIGEST, digest, buffer); return buffer; }
void PolicyHubUpdateKeys(const char *policy_server) { if (GetAmPolicyHub(CFWORKDIR) && NULL != PUBKEY) { unsigned char digest[EVP_MAX_MD_SIZE + 1]; char dst_public_key_filename[CF_BUFSIZE] = ""; { char buffer[CF_HOSTKEY_STRING_SIZE]; HashPubKey(PUBKEY, digest, CF_DEFAULT_DIGEST); snprintf(dst_public_key_filename, sizeof(dst_public_key_filename), "%s/ppkeys/%s-%s.pub", CFWORKDIR, "root", HashPrintSafe(buffer, sizeof(buffer), digest, CF_DEFAULT_DIGEST, true)); MapName(dst_public_key_filename); } struct stat sb; if ((stat(dst_public_key_filename, &sb) == -1)) { char src_public_key_filename[CF_BUFSIZE] = ""; snprintf(src_public_key_filename, CF_MAXVARSIZE, "%s/ppkeys/localhost.pub", CFWORKDIR); MapName(src_public_key_filename); // copy localhost.pub to root-HASH.pub on policy server if (!LinkOrCopy(src_public_key_filename, dst_public_key_filename, false)) { Log(LOG_LEVEL_ERR, "Unable to copy policy server's own public key from '%s' to '%s'", src_public_key_filename, dst_public_key_filename); } if (policy_server) { LastSaw(policy_server, digest, LAST_SEEN_ROLE_CONNECT); } } } }
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); }
/** * @return true the error is not so severe that we must stop */ bool LoadSecretKeys(const char *policy_server) { static char *passphrase = "Cfengine passphrase"; { FILE *fp = fopen(PrivateKeyFile(GetWorkDir()), "r"); if (!fp) { Log(LOG_LEVEL_INFO, "Couldn't find a private key at '%s', use cf-key to get one. (fopen: %s)", PrivateKeyFile(GetWorkDir()), GetErrorStr()); return true; } if ((PRIVKEY = PEM_read_RSAPrivateKey(fp, (RSA **) NULL, NULL, passphrase)) == NULL) { unsigned long err = ERR_get_error(); Log(LOG_LEVEL_ERR, "Error reading private key. (PEM_read_RSAPrivateKey: %s)", ERR_reason_error_string(err)); PRIVKEY = NULL; fclose(fp); return true; } fclose(fp); Log(LOG_LEVEL_VERBOSE, "Loaded private key at '%s'", PrivateKeyFile(GetWorkDir())); } { FILE *fp = fopen(PublicKeyFile(GetWorkDir()), "r"); if (!fp) { Log(LOG_LEVEL_ERR, "Couldn't find a public key at '%s', use cf-key to get one (fopen: %s)", PublicKeyFile(GetWorkDir()), GetErrorStr()); return true; } if ((PUBKEY = PEM_read_RSAPublicKey(fp, NULL, NULL, passphrase)) == NULL) { unsigned long err = ERR_get_error(); Log(LOG_LEVEL_ERR, "Error reading public key at '%s'. (PEM_read_RSAPublicKey: %s)", PublicKeyFile(GetWorkDir()), ERR_reason_error_string(err)); PUBKEY = NULL; fclose(fp); return true; } Log(LOG_LEVEL_VERBOSE, "Loaded public key '%s'", PublicKeyFile(GetWorkDir())); fclose(fp); } if ((BN_num_bits(PUBKEY->e) < 2) || (!BN_is_odd(PUBKEY->e))) { Log(LOG_LEVEL_ERR, "The public key RSA exponent is too small or not odd"); return false; } if (GetAmPolicyHub(CFWORKDIR)) { unsigned char digest[EVP_MAX_MD_SIZE + 1]; char dst_public_key_filename[CF_BUFSIZE] = ""; { char buffer[EVP_MAX_MD_SIZE * 4]; HashPubKey(PUBKEY, digest, CF_DEFAULT_DIGEST); snprintf(dst_public_key_filename, CF_MAXVARSIZE, "%s/ppkeys/%s-%s.pub", CFWORKDIR, "root", HashPrintSafe(CF_DEFAULT_DIGEST, digest, buffer)); MapName(dst_public_key_filename); } struct stat sb; if ((stat(dst_public_key_filename, &sb) == -1)) { char src_public_key_filename[CF_BUFSIZE] = ""; snprintf(src_public_key_filename, CF_MAXVARSIZE, "%s/ppkeys/localhost.pub", CFWORKDIR); MapName(src_public_key_filename); // copy localhost.pub to root-HASH.pub on policy server if (!LinkOrCopy(src_public_key_filename, dst_public_key_filename, false)) { Log(LOG_LEVEL_ERR, "Unable to copy policy server's own public key from '%s' to '%s'", src_public_key_filename, dst_public_key_filename); } if (policy_server) { LastSaw(policy_server, digest, LAST_SEEN_ROLE_CONNECT); } } } return true; }
void IPString2KeyDigest(char *ipv4, char *result) { CF_DB *dbp; CF_DBC *dbcp; char *key; void *value; KeyHostSeen entry; int ksize, vsize; unsigned char digest[EVP_MAX_MD_SIZE + 1]; result[0] = '\0'; if (strcmp(ipv4, "127.0.0.1") == 0 || strcmp(ipv4, "::1") == 0 || strcmp(ipv4, VIPADDRESS) == 0) { if (PUBKEY) { HashPubKey(PUBKEY, digest, CF_DEFAULT_DIGEST); snprintf(result, CF_MAXVARSIZE, "%s", HashPrint(CF_DEFAULT_DIGEST, digest)); } return; } if (!OpenDB(&dbp, dbid_lastseen)) { return; } if (!NewDBCursor(dbp, &dbcp)) { CfOut(cf_inform, "", " !! Unable to scan last-seen database"); CloseDB(dbp); return; } /* Initialize the key/data return pair. */ memset(&entry, 0, sizeof(entry)); /* Walk through the database and print out the key/data pairs. */ while (NextDB(dbp, dbcp, &key, &ksize, &value, &vsize)) { if (value != NULL) { memcpy(&entry, value, sizeof(entry)); // Warning this is not 1:1 if (strcmp(ipv4, MapAddress((char *) entry.address)) == 0) { CfOut(cf_verbose, "", " -> Matched IP %s to key %s", ipv4, key + 1); strncpy(result, key + 1, CF_MAXVARSIZE - 1); break; } } } DeleteDBCursor(dbp, dbcp); CloseDB(dbp); if (NULL_OR_EMPTY(result)) { CfOut(cf_verbose, "", "!! Unable to find a key for ip %s", ipv4); } }
void LoadSecretKeys() { FILE *fp; static char *passphrase = "Cfengine passphrase", name[CF_BUFSIZE], source[CF_BUFSIZE]; char guard[CF_MAXVARSIZE]; unsigned char digest[EVP_MAX_MD_SIZE + 1]; unsigned long err; struct stat sb; if ((fp = fopen(PrivateKeyFile(), "r")) == NULL) { CfOut(OUTPUT_LEVEL_INFORM, "fopen", "Couldn't find a private key (%s) - use cf-key to get one", PrivateKeyFile()); return; } if ((PRIVKEY = PEM_read_RSAPrivateKey(fp, (RSA **) NULL, NULL, passphrase)) == NULL) { err = ERR_get_error(); CfOut(OUTPUT_LEVEL_ERROR, "PEM_read", "Error reading Private Key = %s\n", ERR_reason_error_string(err)); PRIVKEY = NULL; fclose(fp); return; } fclose(fp); CfOut(OUTPUT_LEVEL_VERBOSE, "", " -> Loaded private key %s\n", PrivateKeyFile()); if ((fp = fopen(PublicKeyFile(), "r")) == NULL) { CfOut(OUTPUT_LEVEL_ERROR, "fopen", "Couldn't find a public key (%s) - use cf-key to get one", PublicKeyFile()); return; } if ((PUBKEY = PEM_read_RSAPublicKey(fp, NULL, NULL, passphrase)) == NULL) { err = ERR_get_error(); CfOut(OUTPUT_LEVEL_ERROR, "PEM_read", "Error reading Private Key = %s\n", ERR_reason_error_string(err)); PUBKEY = NULL; fclose(fp); return; } CfOut(OUTPUT_LEVEL_VERBOSE, "", " -> Loaded public key %s\n", PublicKeyFile()); fclose(fp); if ((BN_num_bits(PUBKEY->e) < 2) || (!BN_is_odd(PUBKEY->e))) { FatalError("RSA Exponent too small or not odd"); } if (NULL_OR_EMPTY(POLICY_SERVER)) { snprintf(name, CF_MAXVARSIZE - 1, "%s%cpolicy_server.dat", CFWORKDIR, FILE_SEPARATOR); if ((fp = fopen(name, "r")) != NULL) { if (fscanf(fp, "%4095s", POLICY_SERVER) != 1) { CfDebug("Couldn't read string from policy_server.dat"); } fclose(fp); } } /* Check that we have our own SHA key form of the key in the IP on the hub */ char buffer[EVP_MAX_MD_SIZE * 4]; HashPubKey(PUBKEY, digest, CF_DEFAULT_DIGEST); snprintf(name, CF_MAXVARSIZE, "%s/ppkeys/%s-%s.pub", CFWORKDIR, "root", HashPrintSafe(CF_DEFAULT_DIGEST, digest, buffer)); MapName(name); snprintf(source, CF_MAXVARSIZE, "%s/ppkeys/localhost.pub", CFWORKDIR); MapName(source); // During bootstrap we need the pre-registered IP/hash pair on the hub snprintf(guard, sizeof(guard), "%s/state/am_policy_hub", CFWORKDIR); MapName(guard); // need to use cf_stat if ((stat(name, &sb) == -1) && (stat(guard, &sb) != -1)) // copy localhost.pub to root-HASH.pub on policy server { LastSaw(POLICY_SERVER, digest, LAST_SEEN_ROLE_CONNECT); if (!LinkOrCopy(source, name, false)) { CfOut(OUTPUT_LEVEL_ERROR, "", " -> Unable to clone server's key file as %s\n", name); } } }
int AuthenticateAgent(AgentConnection *conn, bool trust_key) { char sendbuffer[CF_EXPANDSIZE], in[CF_BUFSIZE], *out, *decrypted_cchall; BIGNUM *nonce_challenge, *bn = NULL; unsigned long err; unsigned char digest[EVP_MAX_MD_SIZE]; int encrypted_len, nonce_len = 0, len, session_size; bool implicitly_trust_server; char enterprise_field = 'c'; RSA *server_pubkey = NULL; if ((PUBKEY == NULL) || (PRIVKEY == NULL)) { Log(LOG_LEVEL_ERR, "No public/private key pair found at %s", PublicKeyFile(GetWorkDir())); return false; } enterprise_field = CfEnterpriseOptions(); session_size = CfSessionKeySize(enterprise_field); /* Generate a random challenge to authenticate the server */ nonce_challenge = BN_new(); if (nonce_challenge == NULL) { Log(LOG_LEVEL_ERR, "Cannot allocate BIGNUM structure for server challenge"); return false; } BN_rand(nonce_challenge, CF_NONCELEN, 0, 0); nonce_len = BN_bn2mpi(nonce_challenge, in); if (FIPS_MODE) { HashString(in, nonce_len, digest, CF_DEFAULT_DIGEST); } else { HashString(in, nonce_len, digest, HASH_METHOD_MD5); } /* We assume that the server bound to the remote socket is the official one i.e. = root's */ if ((server_pubkey = HavePublicKeyByIP(conn->username, conn->remoteip))) { implicitly_trust_server = false; encrypted_len = RSA_size(server_pubkey); } else { implicitly_trust_server = true; encrypted_len = nonce_len; } // Server pubkey is what we want to has as a unique ID snprintf(sendbuffer, sizeof(sendbuffer), "SAUTH %c %d %d %c", implicitly_trust_server ? 'n': 'y', encrypted_len, nonce_len, enterprise_field); out = xmalloc(encrypted_len); if (server_pubkey != NULL) { if (RSA_public_encrypt(nonce_len, in, out, server_pubkey, RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); Log(LOG_LEVEL_ERR, "Public encryption failed = %s", ERR_reason_error_string(err)); free(out); RSA_free(server_pubkey); return false; } memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, out, encrypted_len); } else { memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, in, nonce_len); } /* proposition C1 - Send challenge / nonce */ SendTransaction(conn->sd, sendbuffer, CF_RSA_PROTO_OFFSET + encrypted_len, CF_DONE); BN_free(bn); BN_free(nonce_challenge); free(out); if (DEBUG) { RSA_print_fp(stdout, PUBKEY, 0); } /*Send the public key - we don't know if server has it */ /* proposition C2 */ memset(sendbuffer, 0, CF_EXPANDSIZE); len = BN_bn2mpi(PUBKEY->n, sendbuffer); SendTransaction(conn->sd, sendbuffer, len, CF_DONE); /* No need to encrypt the public key ... */ /* proposition C3 */ memset(sendbuffer, 0, CF_EXPANDSIZE); len = BN_bn2mpi(PUBKEY->e, sendbuffer); SendTransaction(conn->sd, sendbuffer, len, CF_DONE); /* check reply about public key - server can break connection here */ /* proposition S1 */ memset(in, 0, CF_BUFSIZE); if (ReceiveTransaction(conn->sd, in, NULL) == -1) { Log(LOG_LEVEL_ERR, "Protocol transaction broken off (1): %s", GetErrorStr()); RSA_free(server_pubkey); return false; } if (BadProtoReply(in)) { Log(LOG_LEVEL_ERR, "%s", in); RSA_free(server_pubkey); return false; } /* Get challenge response - should be CF_DEFAULT_DIGEST of challenge */ /* proposition S2 */ memset(in, 0, CF_BUFSIZE); if (ReceiveTransaction(conn->sd, in, NULL) == -1) { Log(LOG_LEVEL_ERR, "Protocol transaction broken off (2): %s", GetErrorStr()); RSA_free(server_pubkey); return false; } if ((HashesMatch(digest, in, CF_DEFAULT_DIGEST)) || (HashesMatch(digest, in, HASH_METHOD_MD5))) // Legacy { if (implicitly_trust_server == false) /* challenge reply was correct */ { Log(LOG_LEVEL_VERBOSE, ".....................[.h.a.i.l.]................................."); Log(LOG_LEVEL_VERBOSE, "Strong authentication of server=%s connection confirmed", conn->this_server); } else { if (trust_key) { Log(LOG_LEVEL_VERBOSE, " -> Trusting server identity, promise to accept key from %s=%s", conn->this_server, conn->remoteip); } else { Log(LOG_LEVEL_ERR, " !! Not authorized to trust the server=%s's public key (trustkey=false)", conn->this_server); RSA_free(server_pubkey); return false; } } } else { Log(LOG_LEVEL_ERR, "Challenge response from server %s/%s was incorrect!", conn->this_server, conn->remoteip); RSA_free(server_pubkey); return false; } /* Receive counter challenge from server */ /* proposition S3 */ memset(in, 0, CF_BUFSIZE); encrypted_len = ReceiveTransaction(conn->sd, in, NULL); if (encrypted_len <= 0) { Log(LOG_LEVEL_ERR, "Protocol transaction sent illegal cipher length"); RSA_free(server_pubkey); return false; } decrypted_cchall = xmalloc(encrypted_len); if (RSA_private_decrypt(encrypted_len, in, decrypted_cchall, PRIVKEY, RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); Log(LOG_LEVEL_ERR, "Private decrypt failed = %s, abandoning", ERR_reason_error_string(err)); RSA_free(server_pubkey); return false; } /* proposition C4 */ if (FIPS_MODE) { HashString(decrypted_cchall, nonce_len, digest, CF_DEFAULT_DIGEST); } else { HashString(decrypted_cchall, nonce_len, digest, HASH_METHOD_MD5); } if (FIPS_MODE) { SendTransaction(conn->sd, digest, CF_DEFAULT_DIGEST_LEN, CF_DONE); } else { SendTransaction(conn->sd, digest, CF_MD5_LEN, CF_DONE); } free(decrypted_cchall); /* If we don't have the server's public key, it will be sent */ if (server_pubkey == NULL) { RSA *newkey = RSA_new(); Log(LOG_LEVEL_VERBOSE, " -> Collecting public key from server!"); /* proposition S4 - conditional */ if ((len = ReceiveTransaction(conn->sd, in, NULL)) <= 0) { Log(LOG_LEVEL_ERR, "Protocol error in RSA authentation from IP %s", conn->this_server); return false; } if ((newkey->n = BN_mpi2bn(in, len, NULL)) == NULL) { err = ERR_get_error(); Log(LOG_LEVEL_ERR, "Private key decrypt failed = %s", ERR_reason_error_string(err)); RSA_free(newkey); return false; } /* proposition S5 - conditional */ if ((len = ReceiveTransaction(conn->sd, in, NULL)) <= 0) { Log(LOG_LEVEL_INFO, "Protocol error in RSA authentation from IP %s", conn->this_server); RSA_free(newkey); return false; } if ((newkey->e = BN_mpi2bn(in, len, NULL)) == NULL) { err = ERR_get_error(); Log(LOG_LEVEL_ERR, "Public key decrypt failed = %s", ERR_reason_error_string(err)); RSA_free(newkey); return false; } server_pubkey = RSAPublicKey_dup(newkey); RSA_free(newkey); } /* proposition C5 */ if (!SetSessionKey(conn)) { Log(LOG_LEVEL_ERR, "Unable to set session key"); return false; } if (conn->session_key == NULL) { Log(LOG_LEVEL_ERR, "A random session key could not be established"); RSA_free(server_pubkey); return false; } encrypted_len = RSA_size(server_pubkey); out = xmalloc(encrypted_len); if (RSA_public_encrypt(session_size, conn->session_key, out, server_pubkey, RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); Log(LOG_LEVEL_ERR, "Public encryption failed = %s", ERR_reason_error_string(err)); free(out); RSA_free(server_pubkey); return false; } SendTransaction(conn->sd, out, encrypted_len, CF_DONE); if (server_pubkey != NULL) { char buffer[EVP_MAX_MD_SIZE * 4]; HashPubKey(server_pubkey, conn->digest, CF_DEFAULT_DIGEST); Log(LOG_LEVEL_VERBOSE, " -> Public key identity of host \"%s\" is \"%s\"", conn->remoteip, HashPrintSafe(CF_DEFAULT_DIGEST, conn->digest, buffer)); SavePublicKey(conn->username, conn->remoteip, buffer, server_pubkey); // FIXME: username is local LastSaw(conn->remoteip, conn->digest, LAST_SEEN_ROLE_CONNECT); } free(out); RSA_free(server_pubkey); return true; }
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); }
int AuthenticateAgent(AgentConnection *conn, Attributes attr, Promise *pp) { char sendbuffer[CF_EXPANDSIZE], in[CF_BUFSIZE], *out, *decrypted_cchall; BIGNUM *nonce_challenge, *bn = NULL; unsigned long err; unsigned char digest[EVP_MAX_MD_SIZE]; int encrypted_len, nonce_len = 0, len, session_size; char dont_implicitly_trust_server, enterprise_field = 'c'; RSA *server_pubkey = NULL; if (PUBKEY == NULL || PRIVKEY == NULL) { CfOut(cf_error, "", "No public/private key pair found\n"); return false; } enterprise_field = CfEnterpriseOptions(); session_size = CfSessionKeySize(enterprise_field); /* Generate a random challenge to authenticate the server */ nonce_challenge = BN_new(); BN_rand(nonce_challenge, CF_NONCELEN, 0, 0); nonce_len = BN_bn2mpi(nonce_challenge, in); if (FIPS_MODE) { HashString(in, nonce_len, digest, CF_DEFAULT_DIGEST); } else { HashString(in, nonce_len, digest, cf_md5); } /* We assume that the server bound to the remote socket is the official one i.e. = root's */ if ((server_pubkey = HavePublicKeyByIP(conn->username, conn->remoteip))) { dont_implicitly_trust_server = 'y'; encrypted_len = RSA_size(server_pubkey); } else { dont_implicitly_trust_server = 'n'; /* have to trust server, since we can't verify id */ encrypted_len = nonce_len; } // Server pubkey is what we want to has as a unique ID snprintf(sendbuffer, sizeof(sendbuffer), "SAUTH %c %d %d %c", dont_implicitly_trust_server, encrypted_len, nonce_len, enterprise_field); out = xmalloc(encrypted_len); if (server_pubkey != NULL) { if (RSA_public_encrypt(nonce_len, in, out, server_pubkey, RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); cfPS(cf_error, CF_FAIL, "", pp, attr, "Public encryption failed = %s\n", ERR_reason_error_string(err)); free(out); FreeRSAKey(server_pubkey); return false; } memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, out, encrypted_len); } else { memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, in, nonce_len); } /* proposition C1 - Send challenge / nonce */ SendTransaction(conn->sd, sendbuffer, CF_RSA_PROTO_OFFSET + encrypted_len, CF_DONE); BN_free(bn); BN_free(nonce_challenge); free(out); if (DEBUG) { RSA_print_fp(stdout, PUBKEY, 0); } /*Send the public key - we don't know if server has it */ /* proposition C2 */ memset(sendbuffer, 0, CF_EXPANDSIZE); len = BN_bn2mpi(PUBKEY->n, sendbuffer); SendTransaction(conn->sd, sendbuffer, len, CF_DONE); /* No need to encrypt the public key ... */ /* proposition C3 */ memset(sendbuffer, 0, CF_EXPANDSIZE); len = BN_bn2mpi(PUBKEY->e, sendbuffer); SendTransaction(conn->sd, sendbuffer, len, CF_DONE); /* check reply about public key - server can break connection here */ /* proposition S1 */ memset(in, 0, CF_BUFSIZE); if (ReceiveTransaction(conn->sd, in, NULL) == -1) { cfPS(cf_error, CF_INTERPT, "recv", pp, attr, "Protocol transaction broken off (1)"); FreeRSAKey(server_pubkey); return false; } if (BadProtoReply(in)) { CfOut(cf_error, "", "%s", in); FreeRSAKey(server_pubkey); return false; } /* Get challenge response - should be CF_DEFAULT_DIGEST of challenge */ /* proposition S2 */ memset(in, 0, CF_BUFSIZE); if (ReceiveTransaction(conn->sd, in, NULL) == -1) { cfPS(cf_error, CF_INTERPT, "recv", pp, attr, "Protocol transaction broken off (2)"); FreeRSAKey(server_pubkey); return false; } if (HashesMatch(digest, in, CF_DEFAULT_DIGEST) || HashesMatch(digest, in, cf_md5)) // Legacy { if (dont_implicitly_trust_server == 'y') /* challenge reply was correct */ { CfOut(cf_verbose, "", ".....................[.h.a.i.l.].................................\n"); CfOut(cf_verbose, "", "Strong authentication of server=%s connection confirmed\n", pp->this_server); } else { if (attr.copy.trustkey) { CfOut(cf_verbose, "", " -> Trusting server identity, promise to accept key from %s=%s", pp->this_server, conn->remoteip); } else { CfOut(cf_error, "", " !! Not authorized to trust the server=%s's public key (trustkey=false)\n", pp->this_server); PromiseRef(cf_verbose, pp); FreeRSAKey(server_pubkey); return false; } } } else { cfPS(cf_error, CF_INTERPT, "", pp, attr, "Challenge response from server %s/%s was incorrect!", pp->this_server, conn->remoteip); FreeRSAKey(server_pubkey); return false; } /* Receive counter challenge from server */ CfDebug("Receive counter challenge from server\n"); /* proposition S3 */ memset(in, 0, CF_BUFSIZE); encrypted_len = ReceiveTransaction(conn->sd, in, NULL); if (encrypted_len <= 0) { CfOut(cf_error, "", "Protocol transaction sent illegal cipher length"); FreeRSAKey(server_pubkey); return false; } decrypted_cchall = xmalloc(encrypted_len); if (RSA_private_decrypt(encrypted_len, in, decrypted_cchall, PRIVKEY, RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); cfPS(cf_error, CF_INTERPT, "", pp, attr, "Private decrypt failed = %s, abandoning\n", ERR_reason_error_string(err)); FreeRSAKey(server_pubkey); return false; } /* proposition C4 */ if (FIPS_MODE) { HashString(decrypted_cchall, nonce_len, digest, CF_DEFAULT_DIGEST); } else { HashString(decrypted_cchall, nonce_len, digest, cf_md5); } CfDebug("Replying to counter challenge with hash\n"); if (FIPS_MODE) { SendTransaction(conn->sd, digest, CF_DEFAULT_DIGEST_LEN, CF_DONE); } else { SendTransaction(conn->sd, digest, CF_MD5_LEN, CF_DONE); } free(decrypted_cchall); /* If we don't have the server's public key, it will be sent */ if (server_pubkey == NULL) { RSA *newkey = RSA_new(); CfOut(cf_verbose, "", " -> Collecting public key from server!\n"); /* proposition S4 - conditional */ if ((len = ReceiveTransaction(conn->sd, in, NULL)) <= 0) { CfOut(cf_error, "", "Protocol error in RSA authentation from IP %s\n", pp->this_server); return false; } if ((newkey->n = BN_mpi2bn(in, len, NULL)) == NULL) { err = ERR_get_error(); cfPS(cf_error, CF_INTERPT, "", pp, attr, "Private key decrypt failed = %s\n", ERR_reason_error_string(err)); FreeRSAKey(newkey); return false; } /* proposition S5 - conditional */ if ((len = ReceiveTransaction(conn->sd, in, NULL)) <= 0) { cfPS(cf_inform, CF_INTERPT, "", pp, attr, "Protocol error in RSA authentation from IP %s\n", pp->this_server); FreeRSAKey(newkey); return false; } if ((newkey->e = BN_mpi2bn(in, len, NULL)) == NULL) { err = ERR_get_error(); cfPS(cf_error, CF_INTERPT, "", pp, attr, "Public key decrypt failed = %s\n", ERR_reason_error_string(err)); FreeRSAKey(newkey); return false; } server_pubkey = RSAPublicKey_dup(newkey); FreeRSAKey(newkey); } /* proposition C5 */ SetSessionKey(conn); if (conn->session_key == NULL) { CfOut(cf_error, "", "A random session key could not be established"); FreeRSAKey(server_pubkey); return false; } encrypted_len = RSA_size(server_pubkey); CfDebug("Encrypt %d bytes of session key into %d RSA bytes\n", session_size, encrypted_len); out = xmalloc(encrypted_len); if (RSA_public_encrypt(session_size, conn->session_key, out, server_pubkey, RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); cfPS(cf_error, CF_INTERPT, "", pp, attr, "Public encryption failed = %s\n", ERR_reason_error_string(err)); free(out); FreeRSAKey(server_pubkey); return false; } SendTransaction(conn->sd, out, encrypted_len, CF_DONE); if (server_pubkey != NULL) { HashPubKey(server_pubkey, conn->digest, CF_DEFAULT_DIGEST); CfOut(cf_verbose, "", " -> Public key identity of host \"%s\" is \"%s\"", conn->remoteip, HashPrint(CF_DEFAULT_DIGEST, conn->digest)); SavePublicKey(conn->username, conn->remoteip, HashPrint(CF_DEFAULT_DIGEST, conn->digest), server_pubkey); // FIXME: username is local LastSaw(conn->remoteip, conn->digest, cf_connect); } free(out); FreeRSAKey(server_pubkey); return true; }