void LastSaw(char *username, char *ipaddress, unsigned char digest[EVP_MAX_MD_SIZE + 1], enum roles role) { char databuf[CF_BUFSIZE]; char *mapip; 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); mapip = MapAddress(ipaddress); UpdateLastSawHost(databuf, mapip); }
/* Try to accept a connection; handle if we get one. */ static void CFTestD_AcceptAndHandle(int sd, CFTestD_Config *config) { /* TODO embed ConnectionInfo into ServerConnectionState. */ ConnectionInfo *info = ConnectionInfoNew(); /* Uses xcalloc() */ info->ss_len = sizeof(info->ss); info->sd = accept(sd, (struct sockaddr *)&info->ss, &info->ss_len); if (info->sd == -1) { Log(LOG_LEVEL_INFO, "Error accepting connection (%s)", GetErrorStr()); ConnectionInfoDestroy(&info); return; } Log(LOG_LEVEL_DEBUG, "Socket descriptor returned from accept(): %d", info->sd); /* Just convert IP address to string, no DNS lookup. */ char ipaddr[CF_MAX_IP_LEN] = ""; getnameinfo( (const struct sockaddr *)&info->ss, info->ss_len, ipaddr, sizeof(ipaddr), NULL, 0, NI_NUMERICHOST); /* IPv4 mapped addresses (e.g. "::ffff:192.168.1.2") are * hereby represented with their IPv4 counterpart. */ CFTestD_SpawnConnection(MapAddress(ipaddr), info, config); }
uint64_t AllocatePage() { // This one allocates AND maps pages. uint64_t p = AllocatePage_NoMap(); MapAddress(p, p, 0x03); // Do us a favour and zero the memory first. memset((void*) p, 0x00, 0x1000); return p; }
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; } }
void LastSaw(const char *ipaddress, const char *digest, LastSeenRole role) { char databuf[CF_HOSTKEY_STRING_SIZE]; if (strlen(ipaddress) == 0) { Log(LOG_LEVEL_INFO, "LastSeen registry for empty IP with role %d", role); return; } HashPrintSafe(databuf, sizeof(databuf), digest, CF_DEFAULT_DIGEST, true); const char *mapip = MapAddress(ipaddress); UpdateLastSawHost(databuf, mapip, role == LAST_SEEN_ROLE_ACCEPT, time(NULL)); }
void LastSaw(const char *ipaddress, unsigned char digest[EVP_MAX_MD_SIZE + 1], LastSeenRole role) { char databuf[EVP_MAX_MD_SIZE * 4]; if (strlen(ipaddress) == 0) { Log(LOG_LEVEL_INFO, "LastSeen registry for empty IP with role %d", role); return; } HashPrintSafe(CF_DEFAULT_DIGEST, digest, databuf); const char *mapip = MapAddress(ipaddress); UpdateLastSawHost(databuf, mapip, role == LAST_SEEN_ROLE_ACCEPT, time(NULL)); }
Thread::Thread( Process * ps, size_t entry ) { int sid = ps->SpaceId(), tid=0; this->prev = this->next = 0; this->disposed = false; ti = 0; //setup thread information block Tib size_t remote_addr = (size_t)SysAllocateMemory( sid, PAGE_SIZE, MEMORY_ATTR_WRITE, ALLOC_HIGHMEM ); if( remote_addr ==0 ) goto bed; if( MapAddress( sid, SysGetCurrentSpaceId(), remote_addr, (size_t*)&ti ) < 0 ) goto bed; memset( (void*)ti, 0, sizeof(ThreadInformation) ); ti->StackLimit = MB(2); ti->StackBase = (size_t)SysAllocateMemory( sid, ti->StackLimit, MEMORY_ATTR_WRITE, ALLOC_LAZY ); ti->ProcessInformation = ps->GetInformation()->Self; ti->Self = (ThreadInformation*)remote_addr; ti->Environment = 0; ti->ProcessId = ps->ProcessId(); ti->ErrorCode = 0; ti->EntryAddress = entry; ti->SpaceInformation = 0; ti->SpaceId = sid; tid = SysCreateThread( sid, entry, ti->StackLimit, ti->StackBase, (void*)remote_addr ); this->threadId = tid; this->spaceId = sid; ti->ThreadId = tid; if( tid < 0 ) goto bed; this->process = ps; if( ps->mainThread ){ Thread* t; for( t=ps->mainThread; t->next; t=t->next ); t->next = this; this->next = 0; this->prev = t; }else{ this->prev = this->next = 0; ps->mainThread = this; } return; bed: Dispose(); }
void LastSaw(char *ipaddress, unsigned char digest[EVP_MAX_MD_SIZE + 1], enum roles role) { char databuf[CF_BUFSIZE]; char *mapip; if (strlen(ipaddress) == 0) { CfOut(cf_inform, "", "LastSeen registry for empty IP with role %d", role); return; } ThreadLock(cft_output); strlcpy(databuf, HashPrint(CF_DEFAULT_DIGEST, digest), CF_BUFSIZE); ThreadUnlock(cft_output); mapip = MapAddress(ipaddress); UpdateLastSawHost(databuf, mapip, role == cf_accept, time(NULL)); }
/** * @brief Same as LastSaw() but the digest parameter is the hash as a * "SHA=..." string, to avoid converting twice. */ void LastSaw1(const char *ipaddress, const char *hashstr, LastSeenRole role) { const char *mapip = MapAddress(ipaddress); UpdateLastSawHost(hashstr, mapip, role == LAST_SEEN_ROLE_ACCEPT, time(NULL)); }
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); }
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); }
/** * Run this function on every resource (file, class, var etc) access to * grant/deny rights. Currently it checks if: * 1. #ipaddr matches the subnet expression in {admit,deny}_ips * 2. #hostname matches the subdomain expression in {admit,deny}_hostnames * 3. #key is found exactly as it is in {admit,deny}_keys * * @return Default is false, i.e. deny. If a match is found in #acl->admit.* * then return true, unless a match is also found in #acl->deny.* in * which case return false. * * @TODO preprocess our global ACL the moment a client connects, and store in * ServerConnectionState a list of objects that he can access. That way * only his relevant resources will be stored in e.g. {admit,deny}_paths * lists, and running through these two lists on every file request will * be much faster. */ bool access_CheckResource(const struct resource_acl *acl, const char *ipaddr, const char *hostname, const char *key) { /* Only hostname may be NULL in case of resolution failure. */ assert(ipaddr != NULL); assert(key != NULL); size_t pos = (size_t) -1; bool access = false; /* DENY by default */ /* First we check for admission, secondly for denial, so that denial takes * precedence. */ const char *rule; if (acl->admit.ips) { /* Still using legacy code here, doing linear search over all IPs in * textual representation... too CPU intensive! TODO store the ACL as * one list of struct sockaddr_storage, together with CIDR notation * subnet length. */ bool found_rule = false; for (int i = 0; i < StrList_Len(acl->admit.ips); i++) { if (FuzzySetMatch(StrList_At(acl->admit.ips, i), MapAddress(ipaddr)) == 0 || /* Legacy regex matching, TODO DEPRECATE */ StringMatchFull(StrList_At(acl->admit.ips, i), MapAddress(ipaddr))) { found_rule = true; rule = StrList_At(acl->admit.ips, i); break; } } if (found_rule) { Log(LOG_LEVEL_DEBUG, "access_Check: admit IP: %s", rule); access = true; } } if (!access && acl->admit.keys != NULL) { bool ret = StrList_BinarySearch(acl->admit.keys, key, &pos); if (ret) { rule = acl->admit.keys->list[pos]->str; Log(LOG_LEVEL_DEBUG, "access_Check: admit key: %s", rule); access = true; } } if (!access && acl->admit.hostnames != NULL && hostname != NULL) { size_t hostname_len = strlen(hostname); size_t pos = StrList_SearchForPrefix(acl->admit.hostnames, hostname, hostname_len, false); /* === Legacy regex matching, slow, TODO DEPRECATE === */ bool regex_match = false; if (pos == (size_t) -1) { for (int i = 0; i < StrList_Len(acl->admit.hostnames); i++) { if (StringMatchFull(StrList_At(acl->admit.hostnames, i), hostname)) { pos = i; break; } } } /* === === */ if (pos != (size_t) -1) { rule = acl->admit.hostnames->list[pos]->str; size_t rule_len = acl->admit.hostnames->list[pos]->len; /* The rule in the access list has to be an exact match, or be a * subdomain match (i.e. the rule begins with '.') or a regex. */ if (rule_len == hostname_len || rule[0] == '.' || regex_match) { Log(LOG_LEVEL_DEBUG, "access_Check: admit hostname: %s", rule); access = true; } } } /* If access has been granted, we might need to deny it based on ACL. */ if (access && acl->deny.ips != NULL) { bool found_rule = false; for (int i = 0; i < StrList_Len(acl->deny.ips); i++) { if (FuzzySetMatch(StrList_At(acl->deny.ips, i), MapAddress(ipaddr)) == 0 || /* Legacy regex matching, TODO DEPRECATE */ StringMatchFull(StrList_At(acl->deny.ips, i), MapAddress(ipaddr))) { found_rule = true; rule = acl->deny.ips->list[i]->str; break; } } if (found_rule) { Log(LOG_LEVEL_DEBUG, "access_Check: deny IP: %s", rule); access = false; } } if (access && acl->deny.keys != NULL) { bool ret = StrList_BinarySearch(acl->deny.keys, key, &pos); if (ret) { rule = StrList_At(acl->deny.keys, pos); Log(LOG_LEVEL_DEBUG, "access_Check: deny key: %s", rule); access = false; } } if (access && acl->deny.hostnames != NULL && hostname != NULL) { size_t hostname_len = strlen(hostname); size_t pos = StrList_SearchForPrefix(acl->deny.hostnames, hostname, hostname_len, false); /* === Legacy regex matching, slow, TODO DEPRECATE === */ bool regex_match = false; if (pos == (size_t) -1) { for (int i = 0; i < StrList_Len(acl->deny.hostnames); i++) { if (StringMatchFull(StrList_At(acl->deny.hostnames, i), hostname)) { pos = i; break; } } } /* === === */ if (pos != (size_t) -1) { rule = acl->deny.hostnames->list[pos]->str; size_t rule_len = acl->deny.hostnames->list[pos]->len; /* The rule in the access list has to be an exact match, or be a * subdomain match (i.e. the rule begins with '.') or a regex. */ if (rule_len == hostname_len || rule[0] == '.' || regex_match) { Log(LOG_LEVEL_DEBUG, "access_Check: deny hostname: %s", rule); access = false; } } } return access; }
void EarlabModuleInstance::MapToModule(const char *ModuleFileName) { if (ModuleFileName == NULL) throw EarlabException("EarlabModuleInstance: Unable to initialize with NULL module filename"); mModuleFileName = new char [strlen(ModuleFileName) + 1]; strcpy(mModuleFileName, ModuleFileName); LoadModuleFile(ModuleFileName); mReadParameters = (EFIReadParameters *)MapAddress("ReadParameters"); if (mReadParameters == NULL) throw EarlabException("EarlabModuleInstance: Module DLL %s does not contain a ReadParameters entry point", mModuleFileName); mReadParametersEx = (EFIReadParametersEx *) MapAddress("ReadParametersEx"); if (mReadParametersEx == NULL) throw EarlabException("EarlabModuleInstance: Module DLL %s does not contain a ReadParametersEx entry point", mModuleFileName); mStart = (EFIStart *)MapAddress("Start"); if (mStart == NULL) throw EarlabException("EarlabModuleInstance: Module DLL %s does not contain a Start entry point", mModuleFileName); mAdvance = (EFIAdvance *)MapAddress("Advance"); if (mAdvance == NULL) throw EarlabException("EarlabModuleInstance: Module DLL %s does not contain an Advance entry point", mModuleFileName); mStop = (EFIStop *)MapAddress("Stop"); if (mStop == NULL) throw EarlabException("EarlabModuleInstance: Module DLL %s does not contain a Stop entry point", mModuleFileName); mUnload = (EFIUnload *)MapAddress("Unload"); if (mUnload == NULL) throw EarlabException("EarlabModuleInstance: Module DLL %s does not contain an Unload entry point", mModuleFileName); mSetModuleName = (EFISetModuleName *)MapAddress("SetModuleName"); if (mSetModuleName == NULL) throw EarlabException("EarlabModuleInstance: Module DLL %s does not contain a SetModuleName entry point", mModuleFileName); mSetLogger = (EFISetLogger *)MapAddress("SetLogger"); if (mSetLogger == NULL) throw EarlabException("EarlabModuleInstance: Module DLL %s does not contain a SetLogger entry point", mModuleFileName); mSetEnvironment = (EFISetEnvironment *)MapAddress("SetEnvironment"); if (mSetEnvironment == NULL) throw EarlabException("EarlabModuleInstance: Module DLL %s does not contain a SetEnvironment entry point", mModuleFileName); mNewInstance = (EFINewInstance *)MapAddress("NewInstance"); if (mNewInstance == NULL) throw EarlabException("EarlabModuleInstance: Module DLL %s does not contain a NewInstance entry point", mModuleFileName); mDeleteInstance = (EFIDeleteInstance *)MapAddress("DeleteInstance"); if (mDeleteInstance == NULL) throw EarlabException("EarlabModuleInstance: Module DLL %s does not contain a DeleteInstance entry point", mModuleFileName); mInstance = (*mNewInstance)(); }
static int AuthorizeRoles(EvalContext *ctx, ServerConnectionState *conn, char *args) { char *sp; Auth *ap; char userid1[CF_MAXVARSIZE], userid2[CF_MAXVARSIZE]; Rlist *rp, *defines = NULL; int permitted = false; snprintf(userid1, CF_MAXVARSIZE, "%s@%s", conn->username, conn->hostname); snprintf(userid2, CF_MAXVARSIZE, "%s@%s", conn->username, conn->ipaddr); Log(LOG_LEVEL_VERBOSE, "Checking authorized roles in %s", args); if (strncmp(args, "--define", strlen("--define")) == 0) { sp = args + strlen("--define"); } else { sp = args + strlen("-D"); } while (*sp == ' ') { sp++; } defines = RlistFromSplitRegex(ctx, sp, "[,:;]", 99, false); /* For each user-defined class attempt, check RBAC */ for (rp = defines; rp != NULL; rp = rp->next) { Log(LOG_LEVEL_VERBOSE, "Verifying %s", RlistScalarValue(rp)); for (ap = SV.roles; ap != NULL; ap = ap->next) { if (FullTextMatch(ctx, ap->path, RlistScalarValue(rp))) { /* We have a pattern covering this class - so are we allowed to activate it? */ if ((IsMatchItemIn(ctx, ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname)) || (IsRegexItemIn(ctx, ap->accesslist, userid1)) || (IsRegexItemIn(ctx, ap->accesslist, userid2)) || (IsRegexItemIn(ctx, ap->accesslist, conn->username))) { Log(LOG_LEVEL_VERBOSE, "Attempt to define role/class %s is permitted", RlistScalarValue(rp)); permitted = true; } else { Log(LOG_LEVEL_VERBOSE, "Attempt to define role/class %s is denied", RlistScalarValue(rp)); RlistDestroy(defines); return false; } } } } if (permitted) { Log(LOG_LEVEL_VERBOSE, "Role activation allowed"); } else { Log(LOG_LEVEL_VERBOSE, "Role activation disallowed - abort execution"); } RlistDestroy(defines); return permitted; }
int AccessControl(EvalContext *ctx, const char *req_path, ServerConnectionState *conn, int encrypt) { Auth *ap; int access = false; char transrequest[CF_BUFSIZE]; struct stat statbuf; char translated_req_path[CF_BUFSIZE]; char transpath[CF_BUFSIZE]; /* * /var/cfengine -> $workdir translation. */ TranslatePath(translated_req_path, req_path); if (ResolveFilename(translated_req_path, transrequest)) { Log(LOG_LEVEL_VERBOSE, "Filename %s is resolved to %s", translated_req_path, transrequest); } else { Log(LOG_LEVEL_INFO, "Couldn't resolve (realpath: %s) filename: %s", GetErrorStr(), translated_req_path); } if (lstat(transrequest, &statbuf) == -1) { Log(LOG_LEVEL_INFO, "Couldn't stat (lstat: %s) filename: %s", GetErrorStr(), transrequest); return false; } Log(LOG_LEVEL_DEBUG, "AccessControl, match (%s,%s) encrypt request = %d", transrequest, conn->hostname, encrypt); if (SV.admit == NULL) { Log(LOG_LEVEL_INFO, "cf-serverd access list is empty, no files are visible"); return false; } conn->maproot = false; for (ap = SV.admit; ap != NULL; ap = ap->next) { int res = false; Log(LOG_LEVEL_DEBUG, "Examining rule in access list (%s,%s)", transrequest, ap->path); strncpy(transpath, ap->path, CF_BUFSIZE - 1); MapName(transpath); if ((strlen(transrequest) > strlen(transpath)) && (strncmp(transpath, transrequest, strlen(transpath)) == 0) && (transrequest[strlen(transpath)] == FILE_SEPARATOR)) { res = true; /* Substring means must be a / to link, else just a substring og filename */ } /* Exact match means single file to admit */ if (strcmp(transpath, transrequest) == 0) { res = true; } if (strcmp(transpath, "/") == 0) { res = true; } if (res) { Log(LOG_LEVEL_VERBOSE, "Found a matching rule in access list (%s in %s)", transrequest, transpath); if (stat(transpath, &statbuf) == -1) { Log(LOG_LEVEL_INFO, "Warning cannot stat file object %s in admit/grant, or access list refers to dangling link\n", transpath); continue; } if ((!encrypt) && (ap->encrypt == true)) { Log(LOG_LEVEL_ERR, "File %s requires encrypt connection...will not serve", transpath); access = false; } else { Log(LOG_LEVEL_DEBUG, "Checking whether to map root privileges.."); if ((IsMatchItemIn(ctx, ap->maproot, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->maproot, conn->hostname))) { conn->maproot = true; Log(LOG_LEVEL_VERBOSE, "Mapping root privileges to access non-root files"); } if ((IsMatchItemIn(ctx, ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = true; Log(LOG_LEVEL_DEBUG, "Access privileges - match found"); } } break; } } if (strncmp(transpath, transrequest, strlen(transpath)) == 0) { for (ap = SV.deny; ap != NULL; ap = ap->next) { if (IsRegexItemIn(ctx, ap->accesslist, conn->hostname)) { access = false; Log(LOG_LEVEL_INFO, "Host %s explicitly denied access to %s", conn->hostname, transrequest); break; } } } if (access) { Log(LOG_LEVEL_VERBOSE, "Host %s granted access to %s", conn->hostname, req_path); if (encrypt && LOGENCRYPT) { /* Log files that were marked as requiring encryption */ Log(LOG_LEVEL_INFO, "Host %s granted access to %s", conn->hostname, req_path); } } else { Log(LOG_LEVEL_INFO, "Host %s denied access to %s", conn->hostname, req_path); } if (!conn->rsa_auth) { Log(LOG_LEVEL_INFO, "Cannot map root access without RSA authentication"); conn->maproot = false; /* only public files accessible */ } return access; }
/* Checks the "varadmit" legacy ACL. */ static Item *ContextAccessControl(EvalContext *ctx, char *in, ServerConnectionState *conn, int encrypt) { Auth *ap; int access = false; char client_regex[CF_BUFSIZE]; Item *ip, *matches = NULL; int ret = sscanf(in, "CONTEXT %255[^\n]", client_regex); Item *persistent_classes = ListPersistentClasses(); if (ret != 1 || persistent_classes == NULL) { return NULL; } for (ip = persistent_classes; ip != NULL; ip = ip->next) { /* Does the class match the regex that the agent requested? */ if (StringMatchFull(client_regex, ip->name)) { for (ap = SV.varadmit; ap != NULL; ap = ap->next) { /* Does the class match any of the regex in ACLs? */ if (StringMatchFull(ap->path, ip->name)) { Log(LOG_LEVEL_VERBOSE, "Found a matching rule in access list (%s in %s)", ip->name, ap->path); if (ap->classpattern == false) { Log(LOG_LEVEL_ERR, "Context %s requires a literal server item...cannot set variable directly by path", ap->path); access = false; continue; } if ((!encrypt) && (ap->encrypt == true)) { Log(LOG_LEVEL_ERR, "Context %s requires encrypt connection...will not serve", ip->name); access = false; break; } else { Log(LOG_LEVEL_DEBUG, "Checking whether to map root privileges"); if ((IsMatchItemIn(ap->maproot, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->maproot, conn->hostname))) { conn->maproot = true; Log(LOG_LEVEL_VERBOSE, "Mapping root privileges"); } else { Log(LOG_LEVEL_VERBOSE, "No root privileges granted"); } if ((IsMatchItemIn(ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = true; Log(LOG_LEVEL_DEBUG, "Access privileges - match found"); } } } } for (ap = SV.vardeny; ap != NULL; ap = ap->next) { if (strcmp(ap->path, ip->name) == 0) { if ((IsMatchItemIn(ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = false; Log(LOG_LEVEL_VERBOSE, "Host %s explicitly denied access to context %s", conn->hostname, ip->name); break; } } } if (access) { Log(LOG_LEVEL_VERBOSE, "Host %s granted access to context '%s'", conn->hostname, ip->name); AppendItem(&matches, ip->name, NULL); if (encrypt && LOGENCRYPT) { /* Log files that were marked as requiring encryption */ Log(LOG_LEVEL_INFO, "Host %s granted access to context '%s'", conn->hostname, ip->name); } } else { Log(LOG_LEVEL_VERBOSE, "Host %s denied access to context '%s'", conn->hostname, ip->name); } } } DeleteItemList(persistent_classes); return matches; }
/* Checks the "varadmit" legacy ACL. */ static int LiteralAccessControl(EvalContext *ctx, char *in, ServerConnectionState *conn, int encrypt) { Auth *ap; int access = false; char name[CF_BUFSIZE]; name[0] = '\0'; if (strncmp(in, "VAR", 3) == 0) { sscanf(in, "VAR %255[^\n]", name); } else if (strncmp(in, "CALL_ME_BACK", strlen("CALL_ME_BACK")) == 0) { sscanf(in, "CALL_ME_BACK %255[^\n]", name); } else { sscanf(in, "QUERY %128s", name); } conn->maproot = false; for (ap = SV.varadmit; ap != NULL; ap = ap->next) { Log(LOG_LEVEL_VERBOSE, "Examining rule in access list (%s,%s)?", name, ap->path); if (strcmp(ap->path, name) == 0) /* exact match */ { Log(LOG_LEVEL_VERBOSE, "Found a matching rule in access list (%s in %s)", name, ap->path); if ((!ap->literal) && (!ap->variable)) { Log(LOG_LEVEL_ERR, "Variable/query '%s' requires a literal server item...cannot set variable directly by path", ap->path); access = false; break; } if ((!encrypt) && (ap->encrypt == true)) { Log(LOG_LEVEL_ERR, "Variable %s requires encrypt connection...will not serve", name); access = false; break; } else { Log(LOG_LEVEL_DEBUG, "Checking whether to map root privileges"); if ((IsMatchItemIn(ap->maproot, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->maproot, conn->hostname))) { conn->maproot = true; Log(LOG_LEVEL_VERBOSE, "Mapping root privileges"); } else { Log(LOG_LEVEL_VERBOSE, "No root privileges granted"); } if ((IsMatchItemIn(ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = true; Log(LOG_LEVEL_DEBUG, "Access privileges - match found"); } } } } for (ap = SV.vardeny; ap != NULL; ap = ap->next) { if (strcmp(ap->path, name) == 0) { if ((IsMatchItemIn(ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = false; Log(LOG_LEVEL_VERBOSE, "Host %s explicitly denied access to %s", conn->hostname, name); break; } } } if (access) { Log(LOG_LEVEL_VERBOSE, "Host %s granted access to literal '%s'", conn->hostname, name); if (encrypt && LOGENCRYPT) { /* Log files that were marked as requiring encryption */ Log(LOG_LEVEL_INFO, "Host %s granted access to literal '%s'", conn->hostname, name); } } else { Log(LOG_LEVEL_VERBOSE, "Host %s denied access to literal '%s'", conn->hostname, name); } return access; }
static int AccessControl(EvalContext *ctx, const char *req_path, ServerConnectionState *conn, int encrypt) { int access = false; char transrequest[CF_BUFSIZE]; struct stat statbuf; char translated_req_path[CF_BUFSIZE]; char transpath[CF_BUFSIZE]; /* * /var/cfengine -> $workdir translation. */ TranslatePath(translated_req_path, req_path); if (ResolveFilename(translated_req_path, transrequest)) { Log(LOG_LEVEL_VERBOSE, "Filename %s is resolved to %s", translated_req_path, transrequest); } else { Log(LOG_LEVEL_INFO, "Couldn't resolve (realpath: %s) filename: %s", GetErrorStr(), translated_req_path); return false; /* can't continue without transrequest */ } if (lstat(transrequest, &statbuf) == -1) { Log(LOG_LEVEL_INFO, "Couldn't stat (lstat: %s) filename: %s", GetErrorStr(), transrequest); return false; } Log(LOG_LEVEL_DEBUG, "AccessControl, match (%s,%s) encrypt request = %d", transrequest, conn->hostname, encrypt); if (SV.admit == NULL) { Log(LOG_LEVEL_INFO, "cf-serverd access list is empty, no files are visible"); return false; } conn->maproot = false; for (Auth *ap = SV.admit; ap != NULL; ap = ap->next) { int res = false; Log(LOG_LEVEL_DEBUG, "Examining rule in access list (%s,%s)", transrequest, ap->path); /* TODO MapName when constructing this list. */ strncpy(transpath, ap->path, CF_BUFSIZE - 1); MapName(transpath); /* If everything is allowed */ if ((strcmp(transpath, FILE_SEPARATOR_STR) == 0) || /* or if transpath is a parent directory of transrequest */ (strlen(transrequest) > strlen(transpath) && strncmp(transpath, transrequest, strlen(transpath)) == 0 && transrequest[strlen(transpath)] == FILE_SEPARATOR) || /* or if it's an exact match */ (strcmp(transpath, transrequest) == 0)) { res = true; } /* Exact match means single file to admit */ if (strcmp(transpath, transrequest) == 0) { res = true; } if (res) { Log(LOG_LEVEL_VERBOSE, "Found a matching rule in access list (%s in %s)", transrequest, transpath); if (stat(transpath, &statbuf) == -1) { Log(LOG_LEVEL_INFO, "Warning cannot stat file object %s in admit/grant, or access list refers to dangling link", transpath); continue; } if ((!encrypt) && (ap->encrypt == true)) { Log(LOG_LEVEL_ERR, "File %s requires encrypt connection...will not serve", transpath); access = false; } else { Log(LOG_LEVEL_DEBUG, "Checking whether to map root privileges.."); if ((IsMatchItemIn(ap->maproot, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->maproot, conn->hostname))) { conn->maproot = true; Log(LOG_LEVEL_VERBOSE, "Mapping root privileges to access non-root files"); } if ((IsMatchItemIn(ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = true; Log(LOG_LEVEL_DEBUG, "Access granted to host: %s", conn->ipaddr); } } break; } } for (Auth *dp = SV.deny; dp != NULL; dp = dp->next) { strncpy(transpath, dp->path, CF_BUFSIZE - 1); MapName(transpath); /* If everything is denied */ if ((strcmp(transpath, FILE_SEPARATOR_STR) == 0) || /* or if transpath is a parent directory of transrequest */ (strlen(transrequest) > strlen(transpath) && strncmp(transpath, transrequest, strlen(transpath)) == 0 && transrequest[strlen(transpath)] == FILE_SEPARATOR) || /* or if it's an exact match */ (strcmp(transpath, transrequest) == 0)) { if ((IsMatchItemIn(dp->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, dp->accesslist, conn->hostname))) { access = false; Log(LOG_LEVEL_INFO, "Host '%s' in deny list, explicitly denying access to '%s'", conn->ipaddr, transrequest); break; } } } if (access) { Log(LOG_LEVEL_VERBOSE, "Host %s granted access to %s", conn->hostname, req_path); if (encrypt && LOGENCRYPT) { /* Log files that were marked as requiring encryption */ Log(LOG_LEVEL_INFO, "Host %s granted access to %s", conn->hostname, req_path); } } else { Log(LOG_LEVEL_INFO, "Host %s denied access to %s", conn->hostname, req_path); } return access; }
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); } }
/* Wait up to a minute for an in-coming connection. * * @param sd The listening socket or -1. * @retval > 0 In-coming connection. * @retval 0 No in-coming connection. * @retval -1 Error (other than interrupt). * @retval < -1 Interrupted while waiting. */ static int WaitForIncoming(int sd) { Log(LOG_LEVEL_DEBUG, "Waiting at incoming select..."); struct timeval timeout = { .tv_sec = 60 }; int signal_pipe = GetSignalPipe(); fd_set rset; FD_ZERO(&rset); FD_SET(signal_pipe, &rset); /* sd might be -1 if "listen" attribute in body server control is set * to off (enterprise feature for call-collected clients). */ if (sd != -1) { FD_SET(sd, &rset); } int result = select(MAX(sd, signal_pipe) + 1, &rset, NULL, NULL, &timeout); if (result == -1) { return (errno == EINTR) ? -2 : -1; } assert(result >= 0); /* Empty the signal pipe, it is there to only detect missed * signals in-between checking IsPendingTermination() and calling * select(). */ unsigned char buf; while (recv(signal_pipe, &buf, 1, 0) > 0) { /* skip */ } /* We have an incoming connection if select() marked sd as ready: */ if (sd != -1 && result > 0 && FD_ISSET(sd, &rset)) { return 1; } return 0; } /* Check for new policy just before spawning a thread. * * Server reconfiguration can only happen when no threads are active, * so this is a good time to do it; but we do still have to check for * running threads. */ static void PolicyUpdateIfSafe(EvalContext *ctx, Policy **policy, GenericAgentConfig *config) { if (ThreadLock(cft_server_children)) { int prior = COLLECT_INTERVAL; if (ACTIVE_THREADS == 0) { CheckFileChanges(ctx, policy, config); } ThreadUnlock(cft_server_children); /* Check for change in call-collect interval: */ if (prior != COLLECT_INTERVAL) { /* Start, stop or change schedule, as appropriate. */ CollectCallStart(COLLECT_INTERVAL); } } } /* Try to accept a connection; handle if we get one. */ static void AcceptAndHandle(EvalContext *ctx, int sd) { /* TODO embed ConnectionInfo into ServerConnectionState. */ ConnectionInfo *info = ConnectionInfoNew(); /* Uses xcalloc() */ info->ss_len = sizeof(info->ss); info->sd = accept(sd, (struct sockaddr *) &info->ss, &info->ss_len); if (info->sd == -1) { Log(LOG_LEVEL_INFO, "Error accepting connection (%s)", GetErrorStr()); ConnectionInfoDestroy(&info); return; } Log(LOG_LEVEL_DEBUG, "Socket descriptor returned from accept(): %d", info->sd); /* Just convert IP address to string, no DNS lookup. */ char ipaddr[CF_MAX_IP_LEN] = ""; getnameinfo((const struct sockaddr *) &info->ss, info->ss_len, ipaddr, sizeof(ipaddr), NULL, 0, NI_NUMERICHOST); /* IPv4 mapped addresses (e.g. "::ffff:192.168.1.2") are * hereby represented with their IPv4 counterpart. */ ServerEntryPoint(ctx, MapAddress(ipaddr), info); }
Item *ContextAccessControl(EvalContext *ctx, char *in, ServerConnectionState *conn, int encrypt) { Auth *ap; int access = false; char client_regex[CF_BUFSIZE]; CF_DB *dbp; CF_DBC *dbcp; int ksize, vsize; char *key; void *value; time_t now = time(NULL); CfState q; Item *ip, *matches = NULL, *candidates = NULL; sscanf(in, "CONTEXT %255[^\n]", client_regex); if (!OpenDB(&dbp, dbid_state)) { return NULL; } if (!NewDBCursor(dbp, &dbcp)) { Log(LOG_LEVEL_INFO, "Unable to scan persistence cache"); CloseDB(dbp); return NULL; } while (NextDB(dbcp, &key, &ksize, &value, &vsize)) { memcpy((void *) &q, value, sizeof(CfState)); if (now > q.expires) { Log(LOG_LEVEL_VERBOSE, " Persistent class %s expired", key); DBCursorDeleteEntry(dbcp); } else { if (FullTextMatch(ctx, client_regex, key)) { Log(LOG_LEVEL_VERBOSE, " - Found key %s...", key); AppendItem(&candidates, key, NULL); } } } DeleteDBCursor(dbcp); CloseDB(dbp); for (ip = candidates; ip != NULL; ip = ip->next) { for (ap = SV.varadmit; ap != NULL; ap = ap->next) { int res = false; if (FullTextMatch(ctx, ap->path, ip->name)) { res = true; } if (res) { Log(LOG_LEVEL_VERBOSE, "Found a matching rule in access list (%s in %s)", ip->name, ap->path); if (ap->classpattern == false) { Log(LOG_LEVEL_ERR, "Context %s requires a literal server item...cannot set variable directly by path", ap->path); access = false; continue; } if ((!encrypt) && (ap->encrypt == true)) { Log(LOG_LEVEL_ERR, "Context %s requires encrypt connection...will not serve", ip->name); access = false; break; } else { Log(LOG_LEVEL_DEBUG, "Checking whether to map root privileges"); if ((IsMatchItemIn(ctx, ap->maproot, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->maproot, conn->hostname))) { conn->maproot = true; Log(LOG_LEVEL_VERBOSE, "Mapping root privileges"); } else { Log(LOG_LEVEL_VERBOSE, "No root privileges granted"); } if ((IsMatchItemIn(ctx, ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = true; Log(LOG_LEVEL_DEBUG, "Access privileges - match found"); } } } } for (ap = SV.vardeny; ap != NULL; ap = ap->next) { if (strcmp(ap->path, ip->name) == 0) { if ((IsMatchItemIn(ctx, ap->accesslist, MapAddress(conn->ipaddr))) || (IsRegexItemIn(ctx, ap->accesslist, conn->hostname))) { access = false; Log(LOG_LEVEL_VERBOSE, "Host %s explicitly denied access to context %s", conn->hostname, ip->name); break; } } } if (access) { Log(LOG_LEVEL_VERBOSE, "Host %s granted access to context '%s'", conn->hostname, ip->name); AppendItem(&matches, ip->name, NULL); if (encrypt && LOGENCRYPT) { /* Log files that were marked as requiring encryption */ Log(LOG_LEVEL_INFO, "Host %s granted access to context '%s'", conn->hostname, ip->name); } } else { Log(LOG_LEVEL_VERBOSE, "Host %s denied access to context '%s'", conn->hostname, ip->name); } } DeleteItemList(candidates); return matches; }
// How to initialize a process ? int Process::Initialize( uint pid, char* cmdline, char* env ) { Process* p = (Process*)0; uint tid; int result; size_t entry, remote_pi; if( this->disposed ) return -ERR_DISPOSED; //1. create a space for the process uint sid = SysCreateSpace( 0 ); if( sid < 0 ) goto bed; this->spaceId = sid; //3. load executable file to the space! CopyCommandName( this->modulePath, cmdline ); if( (result = PeLoadLibrary( sid, this->modulePath ) ) < 0 ) goto bed; this->module = GetModuleById( result ); if( !this->module ){ printf("## get module by id failed.\n"); goto bed; } //4. setup process information block Pib remote_pi = (size_t)SysAllocateMemory( sid, PAGE_SIZE, MEMORY_ATTR_WRITE, ALLOC_HIGHMEM ); if( remote_pi == 0 ){ printf("Failed in allocating memory.\n"); goto bed; } if( (result=MapAddress( sid, SysGetCurrentSpaceId(), remote_pi, (size_t*)&this->pi )) < 0 ){ printf("Failed in mapping memory.\n"); goto bed; } memset( (void*)pi, 0, sizeof(ProcessInformation) ); pi->ProcessId = sid; pi->UserId = pi->GroupId = 0; pi->ModuleId = this->module->ModuleId; pi->ParentProcessId = 0; pi->EntryAddress = this->module->EntryAddress; pi->SystemInformation = (SystemInformation*)SysGetSystemInformation(); pi->Self = (ProcessInformation*)remote_pi; if( cmdline && (result=MapAddress(SysGetCurrentSpaceId(), sid, (size_t)cmdline, (size_t*)&pi->CommandLine )) < 0 ){ goto bed; } if( env && (result=MapAddress(SysGetCurrentSpaceId(), sid, (size_t)env, (size_t*)&pi->EnvironmentVariables )) < 0 ){ goto bed; } //5. create a main thread printf("[wprocess]sp: %x entry: %x\n", sid, this->module->EntryAddress ); CreateThread( this->module->EntryAddress ); if( this->mainThread==0 ) goto bed; pi->MainThreadId = this->mainThread->ThreadId(); //7. save informations this->commandLine = cmdline; this->environment = env; //8. start main thread to start the process this->mainThread->Resume(); //9. add to tree if( pid ) p = GetProcessById( pid ); if( !p ) p = firstProcess; if( !p ){ firstProcess = this; this->prev = this->next = this->child = this->parent = 0; }else{ while( p->next ) p = p->next; p->next = this; this->prev = p; this->child = 0; this->parent = p->parent; } return 0; bed: printf("[wprocess] failed. result=%d\n", result ); Dispose(); return -ERR_UNKNOWN; }
/** * @brief Accept a TLS connection and authenticate and identify. * @note Various fields in #conn are set, like username and keyhash. */ int ServerTLSSessionEstablish(ServerConnectionState *conn) { int ret; conn->conn_info.ssl = SSL_new(SSLSERVERCONTEXT); if (conn->conn_info.ssl == NULL) { Log(LOG_LEVEL_ERR, "SSL_new: %s", ERR_reason_error_string(ERR_get_error())); return -1; } /* Now we are letting OpenSSL take over the open socket. */ SSL_set_fd(conn->conn_info.ssl, conn->conn_info.sd); ret = SSL_accept(conn->conn_info.ssl); if (ret <= 0) { TLSLogError(conn->conn_info.ssl, LOG_LEVEL_ERR, "Connection handshake", ret); return -1; } Log(LOG_LEVEL_VERBOSE, "TLS cipher negotiated: %s, %s", SSL_get_cipher_name(conn->conn_info.ssl), SSL_get_cipher_version(conn->conn_info.ssl)); Log(LOG_LEVEL_VERBOSE, "TLS session established, checking trust..."); /* Send/Receive "CFE_v%d" version string and agree on version. */ ret = ServerNegotiateProtocol(&conn->conn_info); if (ret <= 0) { return -1; } /* Receive IDENTITY USER=asdf plain string. */ ret = ServerIdentifyClient(&conn->conn_info, conn->username, sizeof(conn->username)); if (ret != 1) { return -1; } /* We *now* (maybe a bit late) verify the key that the client sent us in * the TLS handshake, since we need the username to do so. TODO in the * future store keys irrelevant of username, so that we can match them * before IDENTIFY. */ ret = TLSVerifyPeer(&conn->conn_info, conn->ipaddr, conn->username); if (ret == -1) /* error */ { return -1; } if (ret == 1) /* trusted key */ { Log(LOG_LEVEL_VERBOSE, "%s: Client is TRUSTED, public key MATCHES stored one.", conn->conn_info.remote_keyhash_str); } if (ret == 0) /* untrusted key */ { Log(LOG_LEVEL_WARNING, "%s: Client's public key is UNKNOWN!", conn->conn_info.remote_keyhash_str); if ((SV.trustkeylist != NULL) && (IsMatchItemIn(conn->ctx, SV.trustkeylist, MapAddress(conn->ipaddr)))) { Log(LOG_LEVEL_VERBOSE, "Host %s was found in the \"trustkeysfrom\" list", conn->ipaddr); Log(LOG_LEVEL_WARNING, "%s: Explicitly trusting this key from now on.", conn->conn_info.remote_keyhash_str); conn->trust = true; SavePublicKey("root", conn->conn_info.remote_keyhash_str, conn->conn_info.remote_key); } else { Log(LOG_LEVEL_ERR, "TRUST FAILED, WARNING: possible MAN IN THE MIDDLE attack, dropping connection!"); Log(LOG_LEVEL_ERR, "Open server's ACL if you really want to start trusting this new key."); return -1; } } /* skipping CAUTH */ conn->id_verified = 1; /* skipping SAUTH, allow access to read-only files */ conn->rsa_auth = 1; LastSaw1(conn->ipaddr, conn->conn_info.remote_keyhash_str, LAST_SEEN_ROLE_ACCEPT); ServerSendWelcome(conn); return 1; }