RSA *HavePublicKeyByIP(char *username, char *ipaddress) { char hash[CF_MAXVARSIZE]; Address2Hostkey(ipaddress, hash); return HavePublicKey(username, ipaddress, hash); }
/** * @brief Search for a key given an IP address, by getting the * key hash value from lastseen db. * @return NULL if the key was not found in any form. */ RSA *HavePublicKeyByIP(const char *username, const char *ipaddress) { char hash[CF_HOSTKEY_STRING_SIZE]; /* Get the key hash for that address from lastseen db. */ bool found = Address2Hostkey(hash, sizeof(hash), ipaddress); /* If not found, by passing "" as digest, we effectively look only for * the old-style key file, e.g. root-1.2.3.4.pub. */ return HavePublicKey(username, ipaddress, found ? hash : ""); }
static int CheckStoreKey(ServerConnectionState *conn, RSA *key) { RSA *savedkey; const char *udigest = KeyPrintableHash(ConnectionInfoKey(conn->conn_info)); assert(udigest != NULL); if ((savedkey = HavePublicKey(conn->username, MapAddress(conn->ipaddr), udigest))) { Log(LOG_LEVEL_VERBOSE, "A public key was already known from %s/%s - no trust required", conn->hostname, conn->ipaddr); if ((BN_cmp(savedkey->e, key->e) == 0) && (BN_cmp(savedkey->n, key->n) == 0)) { Log(LOG_LEVEL_VERBOSE, "The public key identity was confirmed as %s@%s", conn->username, conn->hostname); SendTransaction(conn->conn_info, "OK: key accepted", 0, CF_DONE); RSA_free(savedkey); return true; } } /* Finally, if we're still here then the key is new (not in ppkeys * directory): Allow access only if host is listed in "trustkeysfrom" body * server control option. */ if ((SV.trustkeylist != NULL) && (IsMatchItemIn(SV.trustkeylist, MapAddress(conn->ipaddr)))) { Log(LOG_LEVEL_VERBOSE, "Host %s/%s was found in the list of hosts to trust", conn->hostname, conn->ipaddr); SendTransaction(conn->conn_info, "OK: unknown key was accepted on trust", 0, CF_DONE); SavePublicKey(conn->username, udigest, key); return true; } else { Log(LOG_LEVEL_VERBOSE, "No previous key found, and unable to accept this one on trust"); SendTransaction(conn->conn_info, "BAD: key could not be accepted on trust", 0, CF_DONE); return false; } }
static int HailServer(char *host, Attributes a, Promise *pp) { AgentConnection *conn; char sendbuffer[CF_BUFSIZE], recvbuffer[CF_BUFSIZE], peer[CF_MAXVARSIZE], ipv4[CF_MAXVARSIZE], digest[CF_MAXVARSIZE], user[CF_SMALLBUF]; bool gotkey; char reply[8]; a.copy.portnumber = (short) ParseHostname(host, peer); snprintf(ipv4, CF_MAXVARSIZE, "%s", Hostname2IPString(peer)); Address2Hostkey(ipv4, digest); GetCurrentUserName(user, CF_SMALLBUF); if (INTERACTIVE) { CfOut(cf_verbose, "", " -> Using interactive key trust...\n"); gotkey = HavePublicKey(user, peer, digest) != NULL; if (!gotkey) { gotkey = HavePublicKey(user, ipv4, digest) != NULL; } if (!gotkey) { printf("WARNING - You do not have a public key from host %s = %s\n", host, ipv4); printf(" Do you want to accept one on trust? (yes/no)\n\n--> "); while (true) { if (fgets(reply, 8, stdin) == NULL) { FatalError("EOF trying to read answer from terminal"); } if (Chop(reply, CF_EXPANDSIZE) == -1) { CfOut(cf_error, "", "Chop was called on a string that seemed to have no terminator"); } if (strcmp(reply, "yes") == 0) { printf(" -> Will trust the key...\n"); a.copy.trustkey = true; break; } else if (strcmp(reply, "no") == 0) { printf(" -> Will not trust the key...\n"); a.copy.trustkey = false; break; } else { printf(" !! Please reply yes or no...(%s)\n", reply); } } } } /* Continue */ #ifdef __MINGW32__ CfOut(cf_inform, "", "...........................................................................\n"); CfOut(cf_inform, "", " * Hailing %s : %u, with options \"%s\" (serial)\n", peer, a.copy.portnumber, REMOTE_AGENT_OPTIONS); CfOut(cf_inform, "", "...........................................................................\n"); #else /* !__MINGW32__ */ if (BACKGROUND) { CfOut(cf_inform, "", "Hailing %s : %u, with options \"%s\" (parallel)\n", peer, a.copy.portnumber, REMOTE_AGENT_OPTIONS); } else { CfOut(cf_inform, "", "...........................................................................\n"); CfOut(cf_inform, "", " * Hailing %s : %u, with options \"%s\" (serial)\n", peer, a.copy.portnumber, REMOTE_AGENT_OPTIONS); CfOut(cf_inform, "", "...........................................................................\n"); } #endif /* !__MINGW32__ */ a.copy.servers = SplitStringAsRList(peer, '*'); if (a.copy.servers == NULL || strcmp(a.copy.servers->item, "localhost") == 0) { cfPS(cf_inform, CF_NOP, "", pp, a, "No hosts are registered to connect to"); return false; } else { conn = NewServerConnection(a, pp); if (conn == NULL) { DeleteRlist(a.copy.servers); CfOut(cf_verbose, "", " -> No suitable server responded to hail\n"); return false; } } /* Check trust interaction*/ pp->cache = NULL; if (strlen(MENU) > 0) { #if defined(HAVE_NOVA) if (!Nova_ExecuteRunagent(conn, MENU)) { DisconnectServer(conn); DeleteRlist(a.copy.servers); return false; } #endif } else { HailExec(conn, peer, recvbuffer, sendbuffer); } DeleteRlist(a.copy.servers); return true; }
void LastSaw(char *username,char *ipaddress,unsigned char digest[EVP_MAX_MD_SIZE+1],enum roles role) { char databuf[CF_BUFSIZE]; time_t now = time(NULL); int known = false; struct Rlist *rp; struct CfKeyBinding *kp; if (strlen(ipaddress) == 0) { CfOut(cf_inform,"","LastSeen registry for empty IP with role %d",role); return; } ThreadLock(cft_output); switch (role) { case cf_accept: snprintf(databuf,CF_BUFSIZE-1,"-%s",HashPrint(CF_DEFAULT_DIGEST,digest)); break; case cf_connect: snprintf(databuf,CF_BUFSIZE-1,"+%s",HashPrint(CF_DEFAULT_DIGEST,digest)); break; } ThreadUnlock(cft_output); ThreadLock(cft_server_keyseen); for (rp = SERVER_KEYSEEN; rp != NULL; rp=rp->next) { kp = (struct CfKeyBinding *) rp->item; if (strcmp(kp->name,databuf) == 0) { known = true; kp->timestamp = now; CfOut(cf_verbose,""," -> Last saw %s (%s) now",ipaddress,databuf); // Refresh address ThreadLock(cft_system); if (kp->address) { free(kp->address); } kp->address = strdup(ipaddress); ThreadUnlock(cft_system); ThreadUnlock(cft_server_keyseen); return; } } CfOut(cf_verbose,""," -> Last saw %s (%s) first time now",ipaddress,databuf); ThreadLock(cft_system); kp = (struct CfKeyBinding *)malloc((sizeof(struct CfKeyBinding))); ThreadUnlock(cft_system); if (kp == NULL) { ThreadUnlock(cft_server_keyseen); return; } rp = PrependRlist(&SERVER_KEYSEEN,"nothing",CF_SCALAR); ThreadLock(cft_system); free(rp->item); rp->item = kp; kp->address = strdup(ipaddress); if ((kp->name = strdup(databuf)) == NULL) { free(kp); ThreadUnlock(cft_system); ThreadUnlock(cft_server_keyseen); return; } ThreadUnlock(cft_system); kp->key = HavePublicKey(username,ipaddress,databuf+1); kp->timestamp = now; ThreadUnlock(cft_server_keyseen); }
int HailServer(char *host,struct Attributes a,struct Promise *pp) { struct cfagent_connection *conn; char sendbuffer[CF_BUFSIZE],recvbuffer[CF_BUFSIZE],peer[CF_MAXVARSIZE],ipv4[CF_MAXVARSIZE],digest[CF_MAXVARSIZE],user[CF_SMALLBUF]; long gotkey; char reply[8]; struct Item *queries; a.copy.portnumber = (short)ParseHostname(host,peer); snprintf(ipv4,CF_MAXVARSIZE,"%s",Hostname2IPString(peer)); IPString2KeyDigest(ipv4,digest); GetCurrentUserName(user,CF_SMALLBUF); if (INTERACTIVE) { CfOut(cf_verbose,""," -> Using interactive key trust...\n"); gotkey = (long)HavePublicKey(user,peer,digest); if (!gotkey) { gotkey = (long)HavePublicKey(user,ipv4,digest); } if (!gotkey) { printf("WARNING - You do not have a public key from host %s = %s\n",host,ipv4); printf(" Do you want to accept one on trust? (yes/no)\n\n--> "); while (true) { fgets(reply,8,stdin); Chop(reply); if (strcmp(reply,"yes")==0) { printf(" -> Will trust the key...\n"); a.copy.trustkey = true; break; } else if (strcmp(reply,"no")==0) { printf(" -> Will not trust the key...\n"); a.copy.trustkey = false; break; } else { printf(" !! Please reply yes or no...(%s)\n",reply); } } } } /* Continue */ #ifdef MINGW CfOut(cf_inform,"","...........................................................................\n"); CfOut(cf_inform,""," * Hailing %s : %u, with options \"%s\" (serial)\n",peer,a.copy.portnumber,REMOTE_AGENT_OPTIONS); CfOut(cf_inform,"","...........................................................................\n"); #else /* NOT MINGW */ if (BACKGROUND) { CfOut(cf_inform,"","Hailing %s : %u, with options \"%s\" (parallel)\n",peer,a.copy.portnumber,REMOTE_AGENT_OPTIONS); } else { CfOut(cf_inform,"","...........................................................................\n"); CfOut(cf_inform,""," * Hailing %s : %u, with options \"%s\" (serial)\n",peer,a.copy.portnumber,REMOTE_AGENT_OPTIONS); CfOut(cf_inform,"","...........................................................................\n"); } #endif /* NOT MINGW */ a.copy.servers = SplitStringAsRList(peer,'*'); if (a.copy.servers == NULL || strcmp(a.copy.servers->item,"localhost") == 0) { cfPS(cf_inform,CF_NOP,"",pp,a,"No hosts are registered to connect to"); return false; } else { conn = NewServerConnection(a,pp); if (conn == NULL) { CfOut(cf_verbose,""," -> No suitable server responded to hail\n"); return false; } } /* Check trust interaction*/ pp->cache = NULL; if (strlen(MENU) > 0) { #ifdef HAVE_NOVA enum cfd_menu menu = String2Menu(MENU); switch(menu) { case cfd_menu_delta: Nova_QueryForKnowledgeMap(conn,MENU,time(0) - SECONDS_PER_MINUTE * 10); break; case cfd_menu_full: Nova_QueryForKnowledgeMap(conn,MENU,time(0) - SECONDS_PER_WEEK); break; case cfd_menu_relay: #ifdef HAVE_CONSTELLATION queries = Constellation_CreateAllQueries(); Constellation_QueryRelay(conn,queries); DeleteItemList(queries); #endif break; default: break; } #endif /* HAVE_NOVA */ } else { HailExec(conn,peer,recvbuffer,sendbuffer); } ServerDisconnection(conn); DeleteRlist(a.copy.servers); return true; }
static int HailServer(EvalContext *ctx, char *host) { AgentConnection *conn; char sendbuffer[CF_BUFSIZE], recvbuffer[CF_BUFSIZE], peer[CF_MAXVARSIZE], ipv4[CF_MAXVARSIZE], digest[CF_MAXVARSIZE], user[CF_SMALLBUF]; bool gotkey; char reply[8]; FileCopy fc = { .portnumber = (short) ParseHostname(host, peer), }; snprintf(ipv4, CF_MAXVARSIZE, "%s", Hostname2IPString(peer)); Address2Hostkey(ipv4, digest); GetCurrentUserName(user, CF_SMALLBUF); if (INTERACTIVE) { CfOut(OUTPUT_LEVEL_VERBOSE, "", " -> Using interactive key trust...\n"); gotkey = HavePublicKey(user, peer, digest) != NULL; if (!gotkey) { gotkey = HavePublicKey(user, ipv4, digest) != NULL; } if (!gotkey) { printf("WARNING - You do not have a public key from host %s = %s\n", host, ipv4); printf(" Do you want to accept one on trust? (yes/no)\n\n--> "); while (true) { if (fgets(reply, 8, stdin) == NULL) { FatalError(ctx, "EOF trying to read answer from terminal"); } if (Chop(reply, CF_EXPANDSIZE) == -1) { CfOut(OUTPUT_LEVEL_ERROR, "", "Chop was called on a string that seemed to have no terminator"); } if (strcmp(reply, "yes") == 0) { printf(" -> Will trust the key...\n"); fc.trustkey = true; break; } else if (strcmp(reply, "no") == 0) { printf(" -> Will not trust the key...\n"); fc.trustkey = false; break; } else { printf(" !! Please reply yes or no...(%s)\n", reply); } } } } /* Continue */ #ifdef __MINGW32__ CfOut(OUTPUT_LEVEL_INFORM, "", "...........................................................................\n"); CfOut(OUTPUT_LEVEL_INFORM, "", " * Hailing %s : %u, with options \"%s\" (serial)\n", peer, fc.portnumber, REMOTE_AGENT_OPTIONS); CfOut(OUTPUT_LEVEL_INFORM, "", "...........................................................................\n"); #else /* !__MINGW32__ */ if (BACKGROUND) { CfOut(OUTPUT_LEVEL_INFORM, "", "Hailing %s : %u, with options \"%s\" (parallel)\n", peer, fc.portnumber, REMOTE_AGENT_OPTIONS); } else { CfOut(OUTPUT_LEVEL_INFORM, "", "...........................................................................\n"); CfOut(OUTPUT_LEVEL_INFORM, "", " * Hailing %s : %u, with options \"%s\" (serial)\n", peer, fc.portnumber, REMOTE_AGENT_OPTIONS); CfOut(OUTPUT_LEVEL_INFORM, "", "...........................................................................\n"); } #endif /* !__MINGW32__ */ fc.servers = RlistFromSplitString(peer, '*'); if (fc.servers == NULL || strcmp(fc.servers->item, "localhost") == 0) { CfOut(OUTPUT_LEVEL_INFORM, "", "No hosts are registered to connect to"); return false; } else { int err = 0; conn = NewServerConnection(fc, false, &err); if (conn == NULL) { RlistDestroy(fc.servers); CfOut(OUTPUT_LEVEL_VERBOSE, "", " -> No suitable server responded to hail\n"); return false; } } /* Check trust interaction*/ HailExec(conn, peer, recvbuffer, sendbuffer); RlistDestroy(fc.servers); return true; } /********************************************************************/ /* Level 2 */ /********************************************************************/ static void KeepControlPromises(EvalContext *ctx, Policy *policy) { Rval retval; RUNATTR.copy.trustkey = false; RUNATTR.copy.encrypt = true; RUNATTR.copy.force_ipv4 = false; RUNATTR.copy.portnumber = SHORT_CFENGINEPORT; /* Keep promised agent behaviour - control bodies */ Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_RUNAGENT); if (constraints) { for (size_t i = 0; i < SeqLength(constraints); i++) { Constraint *cp = SeqAt(constraints, i); if (!IsDefinedClass(ctx, cp->classes, NULL)) { continue; } if (!EvalContextVariableGet(ctx, (VarRef) { NULL, "control_runagent", cp->lval }, &retval, NULL)) { CfOut(OUTPUT_LEVEL_ERROR, "", "Unknown lval %s in runagent control body", cp->lval); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_FORCE_IPV4].lval) == 0) { RUNATTR.copy.force_ipv4 = BooleanFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET force_ipv4 = %d\n", RUNATTR.copy.force_ipv4); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_TRUSTKEY].lval) == 0) { RUNATTR.copy.trustkey = BooleanFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET trustkey = %d\n", RUNATTR.copy.trustkey); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_ENCRYPT].lval) == 0) { RUNATTR.copy.encrypt = BooleanFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET encrypt = %d\n", RUNATTR.copy.encrypt); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_PORT_NUMBER].lval) == 0) { RUNATTR.copy.portnumber = (short) IntFromString(retval.item); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET default portnumber = %u\n", (int) RUNATTR.copy.portnumber); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_BACKGROUND].lval) == 0) { /* * Only process this option if are is no -b or -i options specified on * command line. */ if (BACKGROUND || INTERACTIVE) { CfOut(OUTPUT_LEVEL_ERROR, "", "Warning: 'background_children' setting from 'body runagent control' is overriden by command-line option."); } else { BACKGROUND = BooleanFromString(retval.item); } continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_MAX_CHILD].lval) == 0) { MAXCHILD = (short) IntFromString(retval.item); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_OUTPUT_TO_FILE].lval) == 0) { OUTPUT_TO_FILE = BooleanFromString(retval.item); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_OUTPUT_DIRECTORY].lval) == 0) { if (IsAbsPath(retval.item)) { strncpy(OUTPUT_DIRECTORY, retval.item, CF_BUFSIZE - 1); CfOut(OUTPUT_LEVEL_VERBOSE, "", "SET output direcory to = %s\n", OUTPUT_DIRECTORY); } continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_TIMEOUT].lval) == 0) { RUNATTR.copy.timeout = (short) IntFromString(retval.item); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_HOSTS].lval) == 0) { if (HOSTLIST == NULL) // Don't override if command line setting { HOSTLIST = retval.item; } continue; } } } if (EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_LASTSEEN_EXPIRE_AFTER, &retval)) { LASTSEENEXPIREAFTER = IntFromString(retval.item) * 60; } }
static int HailServer(const EvalContext *ctx, const GenericAgentConfig *config, char *host) { assert(host != NULL); AgentConnection *conn; char sendbuffer[CF_BUFSIZE], recvbuffer[CF_BUFSIZE], hostkey[CF_HOSTKEY_STRING_SIZE], user[CF_SMALLBUF]; bool gotkey; char reply[8]; bool trustkey = false; char *hostname, *port; ParseHostPort(host, &hostname, &port); if (hostname == NULL || strcmp(hostname, "localhost") == 0) { Log(LOG_LEVEL_INFO, "No remote hosts were specified to connect to"); return false; } if (port == NULL) { port = "5308"; } char ipaddr[CF_MAX_IP_LEN]; if (Hostname2IPString(ipaddr, hostname, sizeof(ipaddr)) == -1) { Log(LOG_LEVEL_ERR, "HailServer: ERROR, could not resolve '%s'", hostname); return false; } Address2Hostkey(hostkey, sizeof(hostkey), ipaddr); GetCurrentUserName(user, CF_SMALLBUF); if (INTERACTIVE) { Log(LOG_LEVEL_VERBOSE, "Using interactive key trust..."); gotkey = HavePublicKey(user, ipaddr, hostkey) != NULL; if (!gotkey) { /* TODO print the hash of the connecting host. But to do that we * should open the connection first, and somehow pass that hash * here! redmine#7212 */ printf("WARNING - You do not have a public key from host %s = %s\n", hostname, ipaddr); printf(" Do you want to accept one on trust? (yes/no)\n\n--> "); while (true) { if (fgets(reply, sizeof(reply), stdin) == NULL) { FatalError(ctx, "EOF trying to read answer from terminal"); } if (Chop(reply, CF_EXPANDSIZE) == -1) { Log(LOG_LEVEL_ERR, "Chop was called on a string that seemed to have no terminator"); } if (strcmp(reply, "yes") == 0) { printf("Will trust the key...\n"); trustkey = true; break; } else if (strcmp(reply, "no") == 0) { printf("Will not trust the key...\n"); trustkey = false; break; } else { printf("Please reply yes or no...(%s)\n", reply); } } } } #ifndef __MINGW32__ if (BACKGROUND) { Log(LOG_LEVEL_INFO, "Hailing %s : %s (in the background)", hostname, port); } else #endif { Log(LOG_LEVEL_INFO, "........................................................................"); Log(LOG_LEVEL_INFO, "Hailing %s : %s", hostname, port); Log(LOG_LEVEL_INFO, "........................................................................"); } ConnectionFlags connflags = { .protocol_version = config->protocol_version, .trust_server = trustkey }; int err = 0; conn = ServerConnection(hostname, port, CONNTIMEOUT, connflags, &err); if (conn == NULL) { Log(LOG_LEVEL_ERR, "Failed to connect to host: %s", hostname); return false; } /* Send EXEC command. */ HailExec(conn, hostname, recvbuffer, sendbuffer); return true; } /********************************************************************/ /* Level 2 */ /********************************************************************/ static void KeepControlPromises(EvalContext *ctx, const Policy *policy) { Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_RUNAGENT); if (constraints) { for (size_t i = 0; i < SeqLength(constraints); i++) { Constraint *cp = SeqAt(constraints, i); if (!IsDefinedClass(ctx, cp->classes)) { continue; } VarRef *ref = VarRefParseFromScope(cp->lval, "control_runagent"); const void *value = EvalContextVariableGet(ctx, ref, NULL); VarRefDestroy(ref); if (!value) { Log(LOG_LEVEL_ERR, "Unknown lval '%s' in runagent control body", cp->lval); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_FORCE_IPV4].lval) == 0) { continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_TRUSTKEY].lval) == 0) { continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_ENCRYPT].lval) == 0) { continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_PORT_NUMBER].lval) == 0) { continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_BACKGROUND].lval) == 0) { /* * Only process this option if are is no -b or -i options specified on * command line. */ if (BACKGROUND || INTERACTIVE) { Log(LOG_LEVEL_WARNING, "'background_children' setting from 'body runagent control' is overridden by command-line option."); } else { BACKGROUND = BooleanFromString(value); } continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_MAX_CHILD].lval) == 0) { MAXCHILD = (short) IntFromString(value); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_OUTPUT_TO_FILE].lval) == 0) { OUTPUT_TO_FILE = BooleanFromString(value); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_OUTPUT_DIRECTORY].lval) == 0) { if (IsAbsPath(value)) { strlcpy(OUTPUT_DIRECTORY, value, CF_BUFSIZE); Log(LOG_LEVEL_VERBOSE, "Setting output direcory to '%s'", OUTPUT_DIRECTORY); } continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_TIMEOUT].lval) == 0) { continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_HOSTS].lval) == 0) { if (HOSTLIST == NULL) // Don't override if command line setting { HOSTLIST = value; } continue; } } } const char *expire_after = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_LASTSEEN_EXPIRE_AFTER); if (expire_after) { LASTSEENEXPIREAFTER = IntFromString(expire_after) * 60; } }
static int HailServer(EvalContext *ctx, char *host) { AgentConnection *conn; char sendbuffer[CF_BUFSIZE], recvbuffer[CF_BUFSIZE], peer[CF_MAXVARSIZE], digest[CF_MAXVARSIZE], user[CF_SMALLBUF]; bool gotkey; char reply[8]; FileCopy fc = { .portnumber = (unsigned short) ParseHostname(host, peer), }; char ipaddr[CF_MAX_IP_LEN]; if (Hostname2IPString(ipaddr, peer, sizeof(ipaddr)) == -1) { Log(LOG_LEVEL_ERR, "HailServer: ERROR, could not resolve '%s'", peer); return false; } Address2Hostkey(ipaddr, digest); GetCurrentUserName(user, CF_SMALLBUF); if (INTERACTIVE) { Log(LOG_LEVEL_VERBOSE, "Using interactive key trust..."); gotkey = HavePublicKey(user, peer, digest) != NULL; if (!gotkey) { gotkey = HavePublicKey(user, ipaddr, digest) != NULL; } if (!gotkey) { printf("WARNING - You do not have a public key from host %s = %s\n", host, ipaddr); printf(" Do you want to accept one on trust? (yes/no)\n\n--> "); while (true) { if (fgets(reply, sizeof(reply), stdin) == NULL) { FatalError(ctx, "EOF trying to read answer from terminal"); } if (Chop(reply, CF_EXPANDSIZE) == -1) { Log(LOG_LEVEL_ERR, "Chop was called on a string that seemed to have no terminator"); } if (strcmp(reply, "yes") == 0) { printf("Will trust the key...\n"); fc.trustkey = true; break; } else if (strcmp(reply, "no") == 0) { printf("Will not trust the key...\n"); fc.trustkey = false; break; } else { printf("Please reply yes or no...(%s)\n", reply); } } } } /* Continue */ #ifdef __MINGW32__ if (LEGACY_OUTPUT) { Log(LOG_LEVEL_INFO, "..........................................................................."); Log(LOG_LEVEL_INFO, " * Hailing %s : %u, with options \"%s\" (serial)", peer, fc.portnumber, REMOTE_AGENT_OPTIONS); Log(LOG_LEVEL_INFO, "..........................................................................."); } else { Log(LOG_LEVEL_INFO, "Hailing '%s' : %u, with options '%s' (serial)", peer, fc.portnumber, REMOTE_AGENT_OPTIONS); } #else /* !__MINGW32__ */ if (BACKGROUND) { Log(LOG_LEVEL_INFO, "Hailing '%s' : %u, with options '%s' (parallel)", peer, fc.portnumber, REMOTE_AGENT_OPTIONS); } else { if (LEGACY_OUTPUT) { Log(LOG_LEVEL_INFO, "..........................................................................."); Log(LOG_LEVEL_INFO, " * Hailing %s : %u, with options \"%s\" (serial)", peer, fc.portnumber, REMOTE_AGENT_OPTIONS); Log(LOG_LEVEL_INFO, "..........................................................................."); } else { Log(LOG_LEVEL_INFO, "Hailing '%s' : %u, with options '%s' (serial)", peer, fc.portnumber, REMOTE_AGENT_OPTIONS); } } #endif /* !__MINGW32__ */ fc.servers = RlistFromSplitString(peer, '*'); if (fc.servers == NULL || strcmp(RlistScalarValue(fc.servers), "localhost") == 0) { Log(LOG_LEVEL_INFO, "No hosts are registered to connect to"); return false; } else { int err = 0; conn = NewServerConnection(fc, false, &err, -1); if (conn == NULL) { RlistDestroy(fc.servers); Log(LOG_LEVEL_VERBOSE, "No suitable server responded to hail"); return false; } } /* Check trust interaction*/ HailExec(conn, peer, recvbuffer, sendbuffer); RlistDestroy(fc.servers); return true; } /********************************************************************/ /* Level 2 */ /********************************************************************/ static void KeepControlPromises(EvalContext *ctx, const Policy *policy) { Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_RUNAGENT); if (constraints) { for (size_t i = 0; i < SeqLength(constraints); i++) { Constraint *cp = SeqAt(constraints, i); if (!IsDefinedClass(ctx, cp->classes)) { continue; } VarRef *ref = VarRefParseFromScope(cp->lval, "control_runagent"); const void *value = EvalContextVariableGet(ctx, ref, NULL); VarRefDestroy(ref); if (!value) { Log(LOG_LEVEL_ERR, "Unknown lval '%s' in runagent control body", cp->lval); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_FORCE_IPV4].lval) == 0) { continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_TRUSTKEY].lval) == 0) { continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_ENCRYPT].lval) == 0) { continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_PORT_NUMBER].lval) == 0) { continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_BACKGROUND].lval) == 0) { /* * Only process this option if are is no -b or -i options specified on * command line. */ if (BACKGROUND || INTERACTIVE) { Log(LOG_LEVEL_WARNING, "'background_children' setting from 'body runagent control' is overridden by command-line option."); } else { BACKGROUND = BooleanFromString(value); } continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_MAX_CHILD].lval) == 0) { MAXCHILD = (short) IntFromString(value); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_OUTPUT_TO_FILE].lval) == 0) { OUTPUT_TO_FILE = BooleanFromString(value); continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_OUTPUT_DIRECTORY].lval) == 0) { if (IsAbsPath(value)) { strncpy(OUTPUT_DIRECTORY, value, CF_BUFSIZE - 1); Log(LOG_LEVEL_VERBOSE, "Setting output direcory to '%s'", OUTPUT_DIRECTORY); } continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_TIMEOUT].lval) == 0) { continue; } if (strcmp(cp->lval, CFR_CONTROLBODY[RUNAGENT_CONTROL_HOSTS].lval) == 0) { if (HOSTLIST == NULL) // Don't override if command line setting { HOSTLIST = value; } continue; } } } const char *expire_after = EvalContextVariableControlCommonGet(ctx, COMMON_CONTROL_LASTSEEN_EXPIRE_AFTER); if (expire_after) { LASTSEENEXPIREAFTER = IntFromString(expire_after) * 60; } }
int KeyAuthentication(struct Image *ip) { 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; char cant_trust_server, keyname[CF_BUFSIZE]; RSA *server_pubkey = NULL; if (COMPATIBILITY_MODE) { return true; } if (PUBKEY == NULL || PRIVKEY == NULL) { CfLog(cferror,"No public/private key pair found\n",""); return false; } /* 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); ChecksumString(in,nonce_len,digest,'m'); /* We assume that the server bound to the remote socket is the official one i.e. = root's */ if (OptionIs(CONTEXTID,"HostnameKeys",true)) { snprintf(keyname,CF_BUFSIZE,"root-%s",ip->server); Debug("KeyAuthentication(with hostname key %s)\n",keyname); } else { snprintf(keyname,CF_BUFSIZE,"root-%s",CONN->remoteip); Debug("KeyAuthentication(with IP keyname %s)\n",keyname); } if (server_pubkey = HavePublicKey(keyname)) { cant_trust_server = 'y'; /* encrypted_len = BN_num_bytes(server_pubkey->n);*/ encrypted_len = RSA_size(server_pubkey); } else { cant_trust_server = 'n'; /* have to trust server, since we can't verify id */ encrypted_len = nonce_len; } snprintf(sendbuffer,CF_BUFSIZE,"SAUTH %c %d %d",cant_trust_server,encrypted_len,nonce_len); if ((out = malloc(encrypted_len)) == NULL) { FatalError("memory failure"); } if (server_pubkey != NULL) { if (RSA_public_encrypt(nonce_len,in,out,server_pubkey,RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); snprintf(OUTPUT,CF_BUFSIZE,"Public encryption failed = %s\n",ERR_reason_error_string(err)); CfLog(cferror,OUTPUT,""); free(out); 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||D2) { 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) { CfLog(cferror,"Protocol transaction broken off",NULL); return false; } if (BadProtoReply(in)) { CfLog(cferror,in,""); return false; } /* Get challenge response - should be md5 of challenge */ /* proposition S2 */ memset(in,0,CF_BUFSIZE); if (ReceiveTransaction(CONN->sd,in,NULL) == -1) { CfLog(cferror,"Protocol transaction broken off",NULL); return false; } if (!ChecksumsMatch(digest,in,'m')) { snprintf(OUTPUT,CF_BUFSIZE,"Challenge response from server %s/%s was incorrect!",ip->server,CONN->remoteip); CfLog(cferror,OUTPUT,""); return false; } else { char server[CF_EXPANDSIZE]; ExpandVarstring(ip->server,server,NULL); if (cant_trust_server == 'y') /* challenge reply was correct */ { Verbose("\n...............................................................\n"); snprintf(OUTPUT,CF_BUFSIZE,"Strong authentication of server=%s connection confirmed\n",server); CfLog(cfverbose,OUTPUT,""); } else { if (ip->trustkey == 'y') { snprintf(OUTPUT,CF_BUFSIZE,"Trusting server identity and willing to accept key from %s=%s",server,CONN->remoteip); CfLog(cferror,OUTPUT,""); } else { snprintf(OUTPUT,CF_BUFSIZE,"Not authorized to trust the server=%s's public key (trustkey=false)\n",server); CfLog(cferror,OUTPUT,""); return false; } } } /* Receive counter challenge from server */ Debug("Receive counter challenge from server\n"); /* proposition S3 */ memset(in,0,CF_BUFSIZE); encrypted_len = ReceiveTransaction(CONN->sd,in,NULL); if (encrypted_len < 0) { CfLog(cferror,"Protocol transaction sent illegal cipher length",NULL); return false; } if ((decrypted_cchall = malloc(encrypted_len)) == NULL) { FatalError("memory failure"); } if (RSA_private_decrypt(encrypted_len,in,decrypted_cchall,PRIVKEY,RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); snprintf(OUTPUT,CF_BUFSIZE,"Private decrypt failed = %s, abandoning\n",ERR_reason_error_string(err)); CfLog(cferror,OUTPUT,""); return false; } /* proposition C4 */ ChecksumString(decrypted_cchall,nonce_len,digest,'m'); Debug("Replying to counter challenge with md5\n"); SendTransaction(CONN->sd,digest,16,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(); Debug("Collecting public key from server!\n"); /* proposition S4 - conditional */ if ((len = ReceiveTransaction(CONN->sd,in,NULL)) <= 0) { CfLog(cferror,"Protocol error in RSA authentation from IP %s\n",ip->server); return false; } if ((newkey->n = BN_mpi2bn(in,len,NULL)) == NULL) { err = ERR_get_error(); snprintf(OUTPUT,CF_BUFSIZE,"Private decrypt failed = %s\n",ERR_reason_error_string(err)); CfLog(cferror,OUTPUT,""); RSA_free(newkey); return false; } /* proposition S5 - conditional */ if ((len=ReceiveTransaction(CONN->sd,in,NULL)) == 0) { CfLog(cfinform,"Protocol error in RSA authentation from IP %s\n",ip->server); RSA_free(newkey); return false; } if ((newkey->e = BN_mpi2bn(in,len,NULL)) == NULL) { err = ERR_get_error(); snprintf(OUTPUT,CF_BUFSIZE,"Private decrypt failed = %s\n",ERR_reason_error_string(err)); CfLog(cferror,OUTPUT,""); RSA_free(newkey); return false; } SavePublicKey(keyname,newkey); server_pubkey = RSAPublicKey_dup(newkey); RSA_free(newkey); } /* proposition C5 */ GenerateRandomSessionKey(); DebugBinOut(CONN->session_key,CF_BLOWFISHSIZE); if (CONN->session_key == NULL) { CfLog(cferror,"A random session key could not be established",""); return false; } else { Debug("Generated session key\n"); DebugBinOut(CONN->session_key,CF_BLOWFISHSIZE); } /* blowfishmpisize = BN_bn2mpi((BIGNUM *)CONN->session_key,in); */ DebugBinOut(CONN->session_key,CF_BLOWFISHSIZE); encrypted_len = RSA_size(server_pubkey); Debug("Encrypt %d to %d\n",CF_BLOWFISHSIZE,encrypted_len); if ((out = malloc(encrypted_len)) == NULL) { FatalError("memory failure"); } if (RSA_public_encrypt(CF_BLOWFISHSIZE,CONN->session_key,out,server_pubkey,RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); snprintf(OUTPUT,CF_BUFSIZE,"Public encryption failed = %s\n",ERR_reason_error_string(err)); CfLog(cferror,OUTPUT,""); free(out); return false; } Debug("Encryption succeeded\n"); SendTransaction(CONN->sd,out,encrypted_len,CF_DONE); DebugBinOut(out,encrypted_len); if (server_pubkey != NULL) { RSA_free(server_pubkey); } free(out); return true; }