bool Command::Execute( void ) { switch( mCmdID ) { case kCmdAssign : return DoAssign(); case kCmdClose : return DoCloseCursor(); case kCmdExec : return DoExec(); case kCmdFetch : return DoFetch(); case kCmdGoIf : return DoGoIf(); case kCmdGoIfNot : return DoGoIfNot(); case kCmdOpen : return DoOpenCursor(); case kCmdParam : return DoReadParam(); case kCmdPrint : return DoPrint(); case kCmdReset : return mpVM->mVariables[ mObject ]->mIsNULL = true; case kCmdReturn : return DoReturn(); case kCmdSelect : return DoSelect(); case kCmdGo : mpVM->mPos = mParam.ToInt(); case kCmdNope : return true; default:; } return false; }
bool DummyDAImpl::Exec(Context &context, ParameterMapper *in, ResultMapper *out) { StartTrace(DummyDAImpl.Exec); return DoExec(context, in, out); }
int BusyWithClassicConnection(EvalContext *ctx, ServerConnectionState *conn) { time_t tloc, trem = 0; char recvbuffer[CF_BUFSIZE + CF_BUFEXT], check[CF_BUFSIZE]; char sendbuffer[CF_BUFSIZE] = { 0 }; char filename[CF_BUFSIZE], buffer[CF_BUFSIZE], args[CF_BUFSIZE], out[CF_BUFSIZE]; long time_no_see = 0; unsigned int len = 0; int drift, plainlen, received, encrypted = 0; ServerFileGetState get_args; Item *classes; memset(recvbuffer, 0, CF_BUFSIZE + CF_BUFEXT); memset(&get_args, 0, sizeof(get_args)); received = ReceiveTransaction(conn->conn_info, recvbuffer, NULL); if (received == -1 || received == 0) { return false; } if (strlen(recvbuffer) == 0) { Log(LOG_LEVEL_WARNING, "Got NULL transmission, skipping!"); return true; } /* Don't process request if we're signalled to exit. */ if (IsPendingTermination()) { return false; } ProtocolCommandClassic command = GetCommandClassic(recvbuffer); switch (command) { /* Plain text authentication; this MUST be the first command client using classic protocol is sending. */ case PROTOCOL_COMMAND_AUTH_PLAIN: SetConnectionData(conn, (char *) (recvbuffer + strlen("CAUTH "))); if (conn->username == NULL || IsUserNameValid(conn->username) == false) { Log(LOG_LEVEL_INFO, "Client is sending wrong username: '******'", conn->username); RefuseAccess(conn, recvbuffer); return false; } /* This is used only for forcing correct state of state machine while connecting and authenticating user using classic protocol. */ conn->user_data_set = true; return true; /* This MUST be exactly second command client using classic protocol is sending. This is where key agreement takes place. */ case PROTOCOL_COMMAND_AUTH_SECURE: /* First command was ommited by client; this is protocol violation. */ if (!conn->user_data_set) { Log(LOG_LEVEL_INFO, "Client is not verified; rejecting connection"); RefuseAccess(conn, recvbuffer); return false; } conn->rsa_auth = AuthenticationDialogue(conn, recvbuffer, received); if (!conn->rsa_auth) { Log(LOG_LEVEL_INFO, "Auth dialogue error"); RefuseAccess(conn, recvbuffer); return false; } return true; default: break; } /* At this point we should have both user_data_set and rsa_auth set to perform any operation. We can check only for second one as without first it won't be set up. */ if (!conn->rsa_auth) { Log(LOG_LEVEL_INFO, "Server refusal due to no RSA authentication [command: %d]", command); RefuseAccess(conn, recvbuffer); return false; } /* We have to have key at this point. */ assert(conn->session_key); /* At this point we can safely do next switch and make sure user is authenticated. */ switch (command) { case PROTOCOL_COMMAND_EXEC: memset(args, 0, CF_BUFSIZE); sscanf(recvbuffer, "EXEC %255[^\n]", args); if (!AllowedUser(conn->username)) { Log(LOG_LEVEL_INFO, "Server refusal due to non-allowed user"); RefuseAccess(conn, recvbuffer); return false; } if (!AccessControl(ctx, CommandArg0(CFRUNCOMMAND), conn, false)) { Log(LOG_LEVEL_INFO, "Server refusal due to denied access to requested object"); RefuseAccess(conn, recvbuffer); return false; } if (!MatchClasses(ctx, conn)) { Log(LOG_LEVEL_INFO, "Server refusal due to failed class/context match"); Terminate(conn->conn_info); return false; } DoExec(ctx, conn, args); Terminate(conn->conn_info); return false; case PROTOCOL_COMMAND_VERSION: snprintf(sendbuffer, sizeof(sendbuffer), "OK: %s", Version()); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); return conn->user_data_set; case PROTOCOL_COMMAND_GET: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "GET %d %[^\n]", &(get_args.buf_size), filename); if ((get_args.buf_size < 0) || (get_args.buf_size > CF_BUFSIZE)) { Log(LOG_LEVEL_INFO, "GET buffer out of bounds"); RefuseAccess(conn, recvbuffer); return false; } if (!AccessControl(ctx, filename, conn, false)) { Log(LOG_LEVEL_INFO, "Access denied to get object"); RefuseAccess(conn, recvbuffer); return true; } memset(sendbuffer, 0, sizeof(sendbuffer)); if (get_args.buf_size >= CF_BUFSIZE) { get_args.buf_size = 2048; } get_args.connect = conn; get_args.encrypt = false; get_args.replybuff = sendbuffer; get_args.replyfile = filename; CfGetFile(&get_args); return true; case PROTOCOL_COMMAND_GET_SECURE: memset(buffer, 0, CF_BUFSIZE); sscanf(recvbuffer, "SGET %u %d", &len, &(get_args.buf_size)); if (received != len + CF_PROTO_OFFSET) { Log(LOG_LEVEL_VERBOSE, "Protocol error SGET"); RefuseAccess(conn, recvbuffer); return false; } plainlen = DecryptString(conn->encryption_type, recvbuffer + CF_PROTO_OFFSET, buffer, conn->session_key, len); cfscanf(buffer, strlen("GET"), strlen("dummykey"), check, sendbuffer, filename); if (strcmp(check, "GET") != 0) { Log(LOG_LEVEL_INFO, "SGET/GET problem"); RefuseAccess(conn, recvbuffer); return true; } if ((get_args.buf_size < 0) || (get_args.buf_size > 8192)) { Log(LOG_LEVEL_INFO, "SGET bounding error"); RefuseAccess(conn, recvbuffer); return false; } if (get_args.buf_size >= CF_BUFSIZE) { get_args.buf_size = 2048; } Log(LOG_LEVEL_DEBUG, "Confirm decryption, and thus validity of caller"); Log(LOG_LEVEL_DEBUG, "SGET '%s' with blocksize %d", filename, get_args.buf_size); if (!AccessControl(ctx, filename, conn, true)) { Log(LOG_LEVEL_INFO, "Access control error"); RefuseAccess(conn, recvbuffer); return false; } memset(sendbuffer, 0, sizeof(sendbuffer)); get_args.connect = conn; get_args.encrypt = true; get_args.replybuff = sendbuffer; get_args.replyfile = filename; CfEncryptGetFile(&get_args); return true; case PROTOCOL_COMMAND_OPENDIR_SECURE: memset(buffer, 0, CF_BUFSIZE); sscanf(recvbuffer, "SOPENDIR %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_VERBOSE, "Protocol error OPENDIR: %d", len); RefuseAccess(conn, recvbuffer); return false; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "OPENDIR", 7) != 0) { Log(LOG_LEVEL_INFO, "Opendir failed to decrypt"); RefuseAccess(conn, recvbuffer); return true; } memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "OPENDIR %[^\n]", filename); if (!AccessControl(ctx, filename, conn, true)) /* opendir don't care about privacy */ { Log(LOG_LEVEL_INFO, "Access error"); RefuseAccess(conn, recvbuffer); return false; } CfSecOpenDirectory(conn, sendbuffer, filename); return true; case PROTOCOL_COMMAND_OPENDIR: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "OPENDIR %[^\n]", filename); if (!AccessControl(ctx, filename, conn, true)) /* opendir don't care about privacy */ { Log(LOG_LEVEL_INFO, "DIR access error"); RefuseAccess(conn, recvbuffer); return false; } CfOpenDirectory(conn, sendbuffer, filename); return true; case PROTOCOL_COMMAND_SYNC_SECURE: memset(buffer, 0, CF_BUFSIZE); sscanf(recvbuffer, "SSYNCH %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_VERBOSE, "Protocol error SSYNCH: %d", len); RefuseAccess(conn, recvbuffer); return false; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (plainlen < 0) { DebugBinOut((char *) conn->session_key, 32, "Session key"); Log(LOG_LEVEL_ERR, "Bad decrypt (%d)", len); } if (strncmp(recvbuffer, "SYNCH", 5) != 0) { Log(LOG_LEVEL_INFO, "No synch"); RefuseAccess(conn, recvbuffer); return true; } /* roll through, no break */ case PROTOCOL_COMMAND_SYNC: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "SYNCH %ld STAT %[^\n]", &time_no_see, filename); trem = (time_t) time_no_see; if ((time_no_see == 0) || (filename[0] == '\0')) { break; } if ((tloc = time((time_t *) NULL)) == -1) { Log(LOG_LEVEL_INFO, "Couldn't read system clock. (time: %s)", GetErrorStr()); SendTransaction(conn->conn_info, "BAD: clocks out of synch", 0, CF_DONE); return true; } drift = (int) (tloc - trem); if (!AccessControl(ctx, filename, conn, true)) { Log(LOG_LEVEL_INFO, "Access control in sync"); RefuseAccess(conn, recvbuffer); return true; } if (DENYBADCLOCKS && (drift * drift > CLOCK_DRIFT * CLOCK_DRIFT)) { snprintf(sendbuffer, sizeof(sendbuffer), "BAD: Clocks are too far unsynchronized %ld/%ld", (long) tloc, (long) trem); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); return true; } else { Log(LOG_LEVEL_DEBUG, "Clocks were off by %ld", (long) tloc - (long) trem); StatFile(conn, sendbuffer, filename); } return true; case PROTOCOL_COMMAND_MD5_SECURE: sscanf(recvbuffer, "SMD5 %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decryption error"); RefuseAccess(conn, recvbuffer); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "MD5", 3) != 0) { Log(LOG_LEVEL_INFO, "MD5 protocol error"); RefuseAccess(conn, recvbuffer); return false; } /* roll through, no break */ case PROTOCOL_COMMAND_MD5: CompareLocalHash(conn, sendbuffer, recvbuffer); return true; case PROTOCOL_COMMAND_VAR_SECURE: sscanf(recvbuffer, "SVAR %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error SVAR"); RefuseAccess(conn, "decrypt error SVAR"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); encrypted = true; if (strncmp(recvbuffer, "VAR", 3) != 0) { Log(LOG_LEVEL_INFO, "VAR protocol defect"); RefuseAccess(conn, "decryption failure"); return false; } /* roll through, no break */ case PROTOCOL_COMMAND_VAR: if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted)) { Log(LOG_LEVEL_INFO, "Literal access failure"); RefuseAccess(conn, recvbuffer); return false; } GetServerLiteral(ctx, conn, sendbuffer, recvbuffer, encrypted); return true; case PROTOCOL_COMMAND_CONTEXT_SECURE: sscanf(recvbuffer, "SCONTEXT %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error SCONTEXT, len,received = %d,%d", len, received); RefuseAccess(conn, "decrypt error SCONTEXT"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); encrypted = true; if (strncmp(recvbuffer, "CONTEXT", 7) != 0) { Log(LOG_LEVEL_INFO, "CONTEXT protocol defect..."); RefuseAccess(conn, "Decryption failed?"); return false; } /* roll through, no break */ case PROTOCOL_COMMAND_CONTEXT: if ((classes = ContextAccessControl(ctx, recvbuffer, conn, encrypted)) == NULL) { Log(LOG_LEVEL_INFO, "Context access failure on %s", recvbuffer); RefuseAccess(conn, recvbuffer); return false; } ReplyServerContext(conn, encrypted, classes); return true; case PROTOCOL_COMMAND_QUERY_SECURE: sscanf(recvbuffer, "SQUERY %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error SQUERY"); RefuseAccess(conn, "decrypt error SQUERY"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "QUERY", 5) != 0) { Log(LOG_LEVEL_INFO, "QUERY protocol defect"); RefuseAccess(conn, "decryption failure"); return false; } if (!LiteralAccessControl(ctx, recvbuffer, conn, true)) { Log(LOG_LEVEL_INFO, "Query access failure"); RefuseAccess(conn, recvbuffer); return false; } if (GetServerQuery(conn, recvbuffer, true)) /* always encrypt */ { return true; } break; case PROTOCOL_COMMAND_CALL_ME_BACK: sscanf(recvbuffer, "SCALLBACK %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error CALL_ME_BACK"); RefuseAccess(conn, "decrypt error CALL_ME_BACK"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "CALL_ME_BACK collect_calls", strlen("CALL_ME_BACK collect_calls")) != 0) { Log(LOG_LEVEL_INFO, "CALL_ME_BACK protocol defect"); RefuseAccess(conn, "decryption failure"); return false; } if (!LiteralAccessControl(ctx, recvbuffer, conn, true)) { Log(LOG_LEVEL_INFO, "Query access failure"); RefuseAccess(conn, recvbuffer); return false; } if (ReceiveCollectCall(conn)) { return true; } case PROTOCOL_COMMAND_AUTH_PLAIN: case PROTOCOL_COMMAND_AUTH_SECURE: case PROTOCOL_COMMAND_AUTH: case PROTOCOL_COMMAND_CONTEXTS: case PROTOCOL_COMMAND_BAD: Log(LOG_LEVEL_WARNING, "Unexpected protocol command"); } strcpy(sendbuffer, "BAD: Request denied"); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); Log(LOG_LEVEL_INFO, "Closing connection, due to request: '%s'", recvbuffer); return false; }
bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) { /* The CF_BUFEXT extra space is there to ensure we're not reading out of * bounds in commands that carry extra binary arguments, like MD5. */ char recvbuffer[CF_BUFSIZE + CF_BUFEXT] = { 0 }; char sendbuffer[CF_BUFSIZE] = { 0 }; char filename[CF_BUFSIZE + 1]; /* +1 for appending slash sometimes */ int received; ServerFileGetState get_args = { 0 }; /* We already encrypt because of the TLS layer, no need to encrypt more. */ const int encrypted = 0; /* Legacy stuff only for old protocol. */ assert(conn->rsa_auth == 1); assert(conn->user_data_set == 1); /* Receive up to CF_BUFSIZE - 1 bytes. */ received = ReceiveTransaction(conn->conn_info, recvbuffer, NULL); if (received == -1 || received == 0) { return false; } if (strlen(recvbuffer) == 0) { Log(LOG_LEVEL_WARNING, "Got NULL transmission, skipping!"); return true; } /* Don't process request if we're signalled to exit. */ if (IsPendingTermination()) { Log(LOG_LEVEL_VERBOSE, "Server must exit, closing connection"); return false; } /* TODO break recvbuffer here: command, param1, param2 etc. */ switch (GetCommandNew(recvbuffer)) { case PROTOCOL_COMMAND_EXEC: { /* TODO check it is always file, never directory, no end with '/' */ char args[256]; int ret = sscanf(recvbuffer, "EXEC %255[^\n]", args); if (ret != 1) /* No arguments, use default args. */ { args[0] = '\0'; } if (!AllowedUser(conn->username)) { Log(LOG_LEVEL_INFO, "EXEC denied due to not allowed user: %s", conn->username); RefuseAccess(conn, recvbuffer); return true; } char arg0[PATH_MAX]; size_t zret = CommandArg0_bound(arg0, CFRUNCOMMAND, sizeof(arg0)); if (zret == (size_t) -1) { goto protocol_error; } zret = PreprocessRequestPath(arg0, sizeof(arg0)); if (zret == (size_t) -1) { goto protocol_error; } /* TODO EXEC should not just use paths_acl access control, but * specific "path_exec" ACL. Then different command execution could be * allowed per host, and the host could even set argv[0] in his EXEC * request, rather than only the arguments. */ if (acl_CheckPath(paths_acl, arg0, conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))) == false) { Log(LOG_LEVEL_INFO, "EXEC denied due to ACL for file: %s", arg0); RefuseAccess(conn, recvbuffer); return true; } if (!MatchClasses(ctx, conn)) { Log(LOG_LEVEL_INFO, "EXEC denied due to failed class match"); Terminate(conn->conn_info); return true; } DoExec(ctx, conn, args); Terminate(conn->conn_info); return true; } case PROTOCOL_COMMAND_VERSION: snprintf(sendbuffer, sizeof(sendbuffer), "OK: %s", Version()); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); return true; case PROTOCOL_COMMAND_GET: { int ret = sscanf(recvbuffer, "GET %d %[^\n]", &(get_args.buf_size), filename); if (ret != 2 || get_args.buf_size <= 0 || get_args.buf_size > CF_BUFSIZE) { goto protocol_error; } Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", "Received:", "GET", filename); /* TODO batch all the following in one function since it's very * similar in all of GET, OPENDIR and STAT. */ size_t zret = ShortcutsExpand(filename, sizeof(filename), SV.path_shortcuts, conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))); if (zret == (size_t) -1) { goto protocol_error; } zret = PreprocessRequestPath(filename, sizeof(filename)); if (zret == (size_t) -1) { goto protocol_error; } PathRemoveTrailingSlash(filename, strlen(filename)); Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", "Translated to:", "GET", filename); if (acl_CheckPath(paths_acl, filename, conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))) == false) { Log(LOG_LEVEL_INFO, "access denied to GET: %s", filename); RefuseAccess(conn, recvbuffer); return true; } memset(sendbuffer, 0, sizeof(sendbuffer)); if (get_args.buf_size >= CF_BUFSIZE) { get_args.buf_size = 2048; } /* TODO eliminate! */ get_args.conn = conn; get_args.encrypt = false; get_args.replybuff = sendbuffer; get_args.replyfile = filename; CfGetFile(&get_args); return true; } case PROTOCOL_COMMAND_OPENDIR: { memset(filename, 0, sizeof(filename)); int ret = sscanf(recvbuffer, "OPENDIR %[^\n]", filename); if (ret != 1) { goto protocol_error; } Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", "Received:", "OPENDIR", filename); /* sizeof()-1 because we need one extra byte for appending '/' afterwards. */ size_t zret = ShortcutsExpand(filename, sizeof(filename) - 1, SV.path_shortcuts, conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))); if (zret == (size_t) -1) { goto protocol_error; } zret = PreprocessRequestPath(filename, sizeof(filename) - 1); if (zret == (size_t) -1) { goto protocol_error; } /* OPENDIR *must* be directory. */ PathAppendTrailingSlash(filename, strlen(filename)); Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", "Translated to:", "OPENDIR", filename); if (acl_CheckPath(paths_acl, filename, conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))) == false) { Log(LOG_LEVEL_INFO, "access denied to OPENDIR: %s", filename); RefuseAccess(conn, recvbuffer); return true; } CfOpenDirectory(conn, sendbuffer, filename); return true; } case PROTOCOL_COMMAND_SYNCH: { long time_no_see = 0; memset(filename, 0, sizeof(filename)); int ret = sscanf(recvbuffer, "SYNCH %ld STAT %[^\n]", &time_no_see, filename); if (ret != 2 || filename[0] == '\0') { goto protocol_error; } time_t tloc = time(NULL); if (tloc == -1) { /* Should never happen. */ Log(LOG_LEVEL_ERR, "Couldn't read system clock. (time: %s)", GetErrorStr()); SendTransaction(conn->conn_info, "BAD: clocks out of synch", 0, CF_DONE); return true; } time_t trem = (time_t) time_no_see; int drift = (int) (tloc - trem); Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", "Received:", "STAT", filename); /* sizeof()-1 because we need one extra byte for appending '/' afterwards. */ size_t zret = ShortcutsExpand(filename, sizeof(filename) - 1, SV.path_shortcuts, conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))); if (zret == (size_t) -1) { goto protocol_error; } zret = PreprocessRequestPath(filename, sizeof(filename) - 1); if (zret == (size_t) -1) { RefuseAccess(conn, recvbuffer); return true; } if (IsDirReal(filename) == 1) { PathAppendTrailingSlash(filename, strlen(filename)); } else { PathRemoveTrailingSlash(filename, strlen(filename)); } Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", "Translated to:", "STAT", filename); if (acl_CheckPath(paths_acl, filename, conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))) == false) { Log(LOG_LEVEL_INFO, "access denied to STAT: %s", filename); RefuseAccess(conn, recvbuffer); return true; } if (DENYBADCLOCKS && (drift * drift > CLOCK_DRIFT * CLOCK_DRIFT)) { snprintf(sendbuffer, sizeof(sendbuffer), "BAD: Clocks are too far unsynchronized %ld/%ld", (long) tloc, (long) trem); Log(LOG_LEVEL_INFO, "denybadclocks %s", sendbuffer); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); return true; } else { Log(LOG_LEVEL_DEBUG, "Clocks were off by %ld", (long) tloc - (long) trem); StatFile(conn, sendbuffer, filename); } return true; } case PROTOCOL_COMMAND_MD5: { int ret = sscanf(recvbuffer, "MD5 %[^\n]", filename); if (ret != 1) { goto protocol_error; } Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", "Received:", "MD5", filename); /* TODO batch all the following in one function since it's very * similar in all of GET, OPENDIR and STAT. */ size_t zret = ShortcutsExpand(filename, sizeof(filename), SV.path_shortcuts, conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))); if (zret == (size_t) -1) { goto protocol_error; } zret = PreprocessRequestPath(filename, sizeof(filename)); if (zret == (size_t) -1) { goto protocol_error; } PathRemoveTrailingSlash(filename, strlen(filename)); Log(LOG_LEVEL_VERBOSE, "%14s %7s %s", "Translated to:", "MD5", filename); if (acl_CheckPath(paths_acl, filename, conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))) == false) { Log(LOG_LEVEL_INFO, "access denied to file: %s", filename); RefuseAccess(conn, recvbuffer); return true; } assert(CF_DEFAULT_DIGEST_LEN <= EVP_MAX_MD_SIZE); unsigned char digest[EVP_MAX_MD_SIZE + 1]; assert(CF_BUFSIZE + CF_SMALL_OFFSET + CF_DEFAULT_DIGEST_LEN <= sizeof(recvbuffer)); memcpy(digest, recvbuffer + strlen(recvbuffer) + CF_SMALL_OFFSET, CF_DEFAULT_DIGEST_LEN); CompareLocalHash(filename, digest, sendbuffer); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); return true; } case PROTOCOL_COMMAND_VAR: { char var[256]; int ret = sscanf(recvbuffer, "VAR %255[^\n]", var); if (ret != 1) { goto protocol_error; } /* TODO if this is literals_acl, then when should I check vars_acl? */ if (acl_CheckExact(literals_acl, var, conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))) == false) { Log(LOG_LEVEL_INFO, "access denied to variable: %s", var); RefuseAccess(conn, recvbuffer); return true; } GetServerLiteral(ctx, conn, sendbuffer, recvbuffer, encrypted); return true; } case PROTOCOL_COMMAND_CONTEXT: { char client_regex[256]; int ret = sscanf(recvbuffer, "CONTEXT %255[^\n]", client_regex); if (ret != 1) { goto protocol_error; } /* WARNING: this comes from legacy code and must be killed if we care * about performance. We should not accept regular expressions from * the client, but this will break backwards compatibility. * * I replicated the code in raw form here to emphasize complexity, * it's the only *slow* command currently in the protocol. */ Item *persistent_classes = ListPersistentClasses(); Item *matched_classes = NULL; /* For all persistent classes */ for (Item *ip = persistent_classes; ip != NULL; ip = ip->next) { const char *class_name = ip->name; /* Does this class match the regex the client sent? */ if (StringMatchFull(client_regex, class_name)) { /* For all ACLs */ for (size_t i = 0; i < classes_acl->len; i++) { struct resource_acl *racl = &classes_acl->acls[i]; /* Does this ACL apply to this host? */ if (access_CheckResource(racl, conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))) == true) { const char *allowed_classes_regex = classes_acl->resource_names->list[i]->str; /* Does this ACL admits access for this class to the * connected host? */ if (StringMatchFull(allowed_classes_regex, class_name)) { PrependItem(&matched_classes, class_name, NULL); } } } } } if (matched_classes == NULL) { Log(LOG_LEVEL_INFO, "No allowed classes for remoteclassesmatching: %s", client_regex); RefuseAccess(conn, recvbuffer); return true; } ReplyServerContext(conn, encrypted, matched_classes); return true; } case PROTOCOL_COMMAND_QUERY: { char query[256], name[128]; int ret1 = sscanf(recvbuffer, "QUERY %255[^\n]", query); int ret2 = sscanf(recvbuffer, "QUERY %127s", name); if (ret1 != 1 || ret2 != 1) { goto protocol_error; } if (acl_CheckExact(query_acl, name, conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))) == false) { Log(LOG_LEVEL_INFO, "access denied to query: %s", query); RefuseAccess(conn, recvbuffer); return true; } if (GetServerQuery(conn, recvbuffer, encrypted)) { return true; } break; } case PROTOCOL_COMMAND_CALL_ME_BACK: if (acl_CheckExact(query_acl, "collect_calls", conn->ipaddr, conn->revdns, KeyPrintableHash(ConnectionInfoKey(conn->conn_info))) == false) { Log(LOG_LEVEL_INFO, "access denied to Call-Collect, check the ACL for class: collect_calls"); return false; } ReceiveCollectCall(conn); /* On success that returned true; otherwise, it did all * relevant Log()ging. Either way, it closed the connection, * so we're no longer busy with it: */ return false; case PROTOCOL_COMMAND_BAD: Log(LOG_LEVEL_WARNING, "Unexpected protocol command: %s", recvbuffer); } /* We should only reach this point if something went really bad, and * close connection. In all other cases (like access denied) connection * shouldn't be closed. * TODO So we need this function to return more than true/false, because * now we return true even when access is denied! E.g. return -1 for * error, 0 on success, 1 on access denied. It can be an option if * connection will close on denial. */ protocol_error: strcpy(sendbuffer, "BAD: Request denied"); SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE); Log(LOG_LEVEL_INFO, "Closing connection due to request: %s", recvbuffer); return false; }
bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn) { time_t tloc, trem = 0; char recvbuffer[CF_BUFSIZE + CF_BUFEXT], sendbuffer[CF_BUFSIZE]; char filename[CF_BUFSIZE], args[CF_BUFSIZE], out[CF_BUFSIZE]; long time_no_see = 0; unsigned int len = 0; int drift, received; ServerFileGetState get_args; Item *classes; /* We never double encrypt within the TLS layer */ const int encrypted = 0; memset(recvbuffer, 0, CF_BUFSIZE + CF_BUFEXT); memset(&get_args, 0, sizeof(get_args)); received = ReceiveTransaction(&conn->conn_info, recvbuffer, NULL); if (received == -1 || received == 0) { return false; } if (strlen(recvbuffer) == 0) { Log(LOG_LEVEL_WARNING, "Got NULL transmission, skipping!"); return true; } /* Don't process request if we're signalled to exit. */ if (IsPendingTermination()) { return false; } switch (GetCommandNew(recvbuffer)) { case PROTOCOL_COMMAND_EXEC: memset(args, 0, CF_BUFSIZE); sscanf(recvbuffer, "EXEC %255[^\n]", args); if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "Server refusal due to incorrect identity"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!AllowedUser(conn->username)) { Log(LOG_LEVEL_INFO, "Server refusal due to non-allowed user"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!conn->rsa_auth) { Log(LOG_LEVEL_INFO, "Server refusal due to no RSA authentication"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!AccessControl(ctx, CommandArg0(CFRUNCOMMAND), conn, false)) { Log(LOG_LEVEL_INFO, "Server refusal due to denied access to requested object"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!MatchClasses(ctx, conn)) { Log(LOG_LEVEL_INFO, "Server refusal due to failed class/context match"); Terminate(&conn->conn_info); return false; } DoExec(ctx, conn, args); Terminate(&conn->conn_info); return false; case PROTOCOL_COMMAND_VERSION: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); } snprintf(conn->output, CF_BUFSIZE, "OK: %s", Version()); SendTransaction(&conn->conn_info, conn->output, 0, CF_DONE); return conn->id_verified; case PROTOCOL_COMMAND_GET: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "GET %d %[^\n]", &(get_args.buf_size), filename); if ((get_args.buf_size < 0) || (get_args.buf_size > CF_BUFSIZE)) { Log(LOG_LEVEL_INFO, "GET buffer out of bounds"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!AccessControl(ctx, filename, conn, false)) { Log(LOG_LEVEL_INFO, "Access denied to get object"); RefuseAccess(conn, 0, recvbuffer); return true; } memset(sendbuffer, 0, CF_BUFSIZE); if (get_args.buf_size >= CF_BUFSIZE) { get_args.buf_size = 2048; } get_args.connect = conn; get_args.encrypt = false; get_args.replybuff = sendbuffer; get_args.replyfile = filename; CfGetFile(&get_args); return true; case PROTOCOL_COMMAND_OPENDIR: memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "OPENDIR %[^\n]", filename); if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return false; } if (!AccessControl(ctx, filename, conn, true)) /* opendir don't care about privacy */ { Log(LOG_LEVEL_INFO, "DIR access error"); RefuseAccess(conn, 0, recvbuffer); return false; } CfOpenDirectory(conn, sendbuffer, filename); return true; case PROTOCOL_COMMAND_SYNC: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return false; } memset(filename, 0, CF_BUFSIZE); sscanf(recvbuffer, "SYNCH %ld STAT %[^\n]", &time_no_see, filename); trem = (time_t) time_no_see; if ((time_no_see == 0) || (filename[0] == '\0')) { break; } if ((tloc = time((time_t *) NULL)) == -1) { sprintf(conn->output, "Couldn't read system clock\n"); Log(LOG_LEVEL_INFO, "Couldn't read system clock. (time: %s)", GetErrorStr()); SendTransaction(&conn->conn_info, "BAD: clocks out of synch", 0, CF_DONE); return true; } drift = (int) (tloc - trem); if (!AccessControl(ctx, filename, conn, true)) { Log(LOG_LEVEL_VERBOSE, "AccessControl: access denied"); RefuseAccess(conn, 0, recvbuffer); return true; } if (DENYBADCLOCKS && (drift * drift > CLOCK_DRIFT * CLOCK_DRIFT)) { snprintf(conn->output, CF_BUFSIZE - 1, "BAD: Clocks are too far unsynchronized %ld/%ld\n", (long) tloc, (long) trem); SendTransaction(&conn->conn_info, conn->output, 0, CF_DONE); return true; } else { Log(LOG_LEVEL_DEBUG, "Clocks were off by %ld", (long) tloc - (long) trem); StatFile(conn, sendbuffer, filename); } return true; case PROTOCOL_COMMAND_MD5: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return true; } CompareLocalHash(conn, sendbuffer, recvbuffer); return true; case PROTOCOL_COMMAND_VAR: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return true; } if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted)) { Log(LOG_LEVEL_INFO, "Literal access failure"); RefuseAccess(conn, 0, recvbuffer); return false; } GetServerLiteral(ctx, conn, sendbuffer, recvbuffer, encrypted); return true; case PROTOCOL_COMMAND_CONTEXT: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, "Context probe"); return true; } if ((classes = ContextAccessControl(ctx, recvbuffer, conn, encrypted)) == NULL) { Log(LOG_LEVEL_INFO, "Context access failure on %s", recvbuffer); RefuseAccess(conn, 0, recvbuffer); return false; } ReplyServerContext(conn, encrypted, classes); return true; case PROTOCOL_COMMAND_QUERY: if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return true; } if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted)) { Log(LOG_LEVEL_INFO, "Query access failure"); RefuseAccess(conn, 0, recvbuffer); return false; } if (GetServerQuery(conn, recvbuffer, encrypted)) { return true; } break; case PROTOCOL_COMMAND_CALL_ME_BACK: sscanf(recvbuffer, "SCALLBACK %u", &len); if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET))) { Log(LOG_LEVEL_INFO, "Decrypt error CALL_ME_BACK"); RefuseAccess(conn, 0, "decrypt error CALL_ME_BACK"); return true; } memcpy(out, recvbuffer + CF_PROTO_OFFSET, len); DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len); if (strncmp(recvbuffer, "CALL_ME_BACK collect_calls", strlen("CALL_ME_BACK collect_calls")) != 0) { Log(LOG_LEVEL_INFO, "CALL_ME_BACK protocol defect"); RefuseAccess(conn, 0, "decryption failure"); return false; } if (!conn->id_verified) { Log(LOG_LEVEL_INFO, "ID not verified"); RefuseAccess(conn, 0, recvbuffer); return true; } if (!LiteralAccessControl(ctx, recvbuffer, conn, true)) { Log(LOG_LEVEL_INFO, "Query access failure"); RefuseAccess(conn, 0, recvbuffer); return false; } return ReceiveCollectCall(conn); case PROTOCOL_COMMAND_BAD: Log(LOG_LEVEL_WARNING, "Unexpected protocol command: %s", recvbuffer); } sprintf(sendbuffer, "BAD: Request denied\n"); SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE); Log(LOG_LEVEL_INFO, "Closing connection, due to request: '%s'", recvbuffer); return false; }