Esempio n. 1
0
void ReplyServerContext(ServerConnectionState *conn, int encrypted, Item *classes)
{
    char out[CF_BUFSIZE];
    int cipherlen;
    Item *ip;

    char sendbuffer[CF_BUFSIZE];
    memset(sendbuffer, 0, CF_BUFSIZE);

    for (ip = classes; ip != NULL; ip = ip->next)
    {
        if (strlen(sendbuffer) + strlen(ip->name) < CF_BUFSIZE - 3)
        {
            strcat(sendbuffer, ip->name);
            strcat(sendbuffer, ",");
        }
        else
        {
            Log(LOG_LEVEL_ERR, "Overflow in context grab");
            break;
        }
    }

    DeleteItemList(classes);

    if (encrypted)
    {
        cipherlen = EncryptString(conn->encryption_type, sendbuffer, out, conn->session_key, strlen(sendbuffer) + 1);
        SendTransaction(&conn->conn_info, out, cipherlen, CF_DONE);
    }
    else
    {
        SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
    }
}
Esempio n. 2
0
void GetServerLiteral(EvalContext *ctx, ServerConnectionState *conn, char *sendbuffer, char *recvbuffer, int encrypted)
{
    char handle[CF_BUFSIZE], out[CF_BUFSIZE];
    int cipherlen;

    sscanf(recvbuffer, "VAR %255[^\n]", handle);

    if (ReturnLiteralData(ctx, handle, out))
    {
        memset(sendbuffer, 0, CF_BUFSIZE);
        snprintf(sendbuffer, CF_BUFSIZE - 1, "%s", out);
    }
    else
    {
        memset(sendbuffer, 0, CF_BUFSIZE);
        snprintf(sendbuffer, CF_BUFSIZE - 1, "BAD: Not found");
    }

    if (encrypted)
    {
        cipherlen = EncryptString(conn->encryption_type, sendbuffer, out, conn->session_key, strlen(sendbuffer) + 1);
        SendTransaction(&conn->conn_info, out, cipherlen, CF_DONE);
    }
    else
    {
        SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
    }
}
Esempio n. 3
0
int CfSecOpenDirectory(ServerConnectionState *conn, char *sendbuffer, char *dirname)
{
    Dir *dirh;
    const struct dirent *dirp;
    int offset, cipherlen;
    char out[CF_BUFSIZE];

    if (!IsAbsoluteFileName(dirname))
    {
        sprintf(sendbuffer, "BAD: request to access a non-absolute filename\n");
        cipherlen = EncryptString(conn->encryption_type, sendbuffer, out, conn->session_key, strlen(sendbuffer) + 1);
        SendTransaction(&conn->conn_info, out, cipherlen, CF_DONE);
        return -1;
    }

    if ((dirh = DirOpen(dirname)) == NULL)
    {
        Log(LOG_LEVEL_VERBOSE, "Couldn't open dir %s", dirname);
        snprintf(sendbuffer, CF_BUFSIZE, "BAD: cfengine, couldn't open dir %s\n", dirname);
        cipherlen = EncryptString(conn->encryption_type, sendbuffer, out, conn->session_key, strlen(sendbuffer) + 1);
        SendTransaction(&conn->conn_info, out, cipherlen, CF_DONE);
        return -1;
    }

/* Pack names for transmission */

    memset(sendbuffer, 0, CF_BUFSIZE);

    offset = 0;

    for (dirp = DirRead(dirh); dirp != NULL; dirp = DirRead(dirh))
    {
        if (strlen(dirp->d_name) + 1 + offset >= CF_BUFSIZE - CF_MAXLINKSIZE)
        {
            cipherlen = EncryptString(conn->encryption_type, sendbuffer, out, conn->session_key, offset + 1);
            SendTransaction(&conn->conn_info, out, cipherlen, CF_MORE);
            offset = 0;
            memset(sendbuffer, 0, CF_BUFSIZE);
            memset(out, 0, CF_BUFSIZE);
        }

        strncpy(sendbuffer + offset, dirp->d_name, CF_MAXLINKSIZE);
        /* + zero byte separator */
        offset += strlen(dirp->d_name) + 1;
    }

    strcpy(sendbuffer + offset, CFD_TERMINATOR);

    cipherlen =
        EncryptString(conn->encryption_type, sendbuffer, out, conn->session_key, offset + 2 + strlen(CFD_TERMINATOR));
    SendTransaction(&conn->conn_info, out, cipherlen, CF_DONE);
    DirClose(dirh);
    return 0;
}
Esempio n. 4
0
int CfOpenDirectory(ServerConnectionState *conn, char *sendbuffer, char *oldDirname)
{
    Dir *dirh;
    const struct dirent *dirp;
    int offset;
    char dirname[CF_BUFSIZE];

    TranslatePath(dirname, oldDirname);

    if (!IsAbsoluteFileName(dirname))
    {
        sprintf(sendbuffer, "BAD: request to access a non-absolute filename\n");
        SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
        return -1;
    }

    if ((dirh = DirOpen(dirname)) == NULL)
    {
        Log(LOG_LEVEL_DEBUG, "Couldn't open dir '%s'", dirname);
        snprintf(sendbuffer, CF_BUFSIZE, "BAD: cfengine, couldn't open dir %s\n", dirname);
        SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
        return -1;
    }

/* Pack names for transmission */

    memset(sendbuffer, 0, CF_BUFSIZE);

    offset = 0;

    for (dirp = DirRead(dirh); dirp != NULL; dirp = DirRead(dirh))
    {
        if (strlen(dirp->d_name) + 1 + offset >= CF_BUFSIZE - CF_MAXLINKSIZE)
        {
            SendTransaction(&conn->conn_info, sendbuffer, offset + 1, CF_MORE);
            offset = 0;
            memset(sendbuffer, 0, CF_BUFSIZE);
        }

        strncpy(sendbuffer + offset, dirp->d_name, CF_MAXLINKSIZE);
        offset += strlen(dirp->d_name) + 1;     /* + zero byte separator */
    }

    strcpy(sendbuffer + offset, CFD_TERMINATOR);
    SendTransaction(&conn->conn_info, sendbuffer, offset + 2 + strlen(CFD_TERMINATOR), CF_DONE);
    DirClose(dirh);
    return 0;
}
Esempio n. 5
0
void RefuseAccess(ServerConnectionState *conn, int size, char *errmesg)
{
    char *username, *ipaddr;
    char *def = "?";

    if (strlen(conn->username) == 0)
    {
        username = def;
    }
    else
    {
        username = conn->username;
    }

    if (strlen(conn->ipaddr) == 0)
    {
        ipaddr = def;
    }
    else
    {
        ipaddr = conn->ipaddr;
    }

    char sendbuffer[CF_BUFSIZE];
    snprintf(sendbuffer, CF_BUFSIZE, "%s", CF_FAILEDSTR);
    SendTransaction(&conn->conn_info, sendbuffer, size, CF_DONE);

    Log(LOG_LEVEL_INFO, "REFUSAL to (user=%s,ip=%s) of request: %s",
        username, ipaddr, errmesg);
}
Esempio n. 6
0
bool CFTestD_GetServerQuery(
    ServerConnectionState *conn, char *recvbuffer, CFTestD_Config *config)
{
    char query[CF_BUFSIZE];
    Seq *report          = config->report;
    const int report_len = config->report_len;
    ConnectionInfo *const conn_info = conn->conn_info;

    query[0] = '\0';
    sscanf(recvbuffer, "QUERY %255[^\n]", query);

    if (strlen(query) == 0)
    {
        return false;
    }

    if (report_len == 0)
    {
        Log(LOG_LEVEL_INFO,
            "No report file argument so returning canned data from enterprise plugin server\n");
        return CFTestD_ReturnQueryData(conn, query);
    }

    Log(LOG_LEVEL_INFO,
        "Report file argument specified. Returning report of length %d",
        report_len);

    const size_t n_report_lines = SeqLength(report);

    assert(n_report_lines > 1);
    char *ts = SeqAt(report, 0);
    char *header = StringFormat("CFR: 0 %s %d\n", ts, report_len);
    SendTransaction(conn_info, header, SafeStringLength(header), CF_MORE);
    free(header);

    for (size_t i = 1; i < n_report_lines; i++)
    {
        const char *report_line = SeqAt(report, i);
        SendTransaction(conn_info, report_line, SafeStringLength(report_line), CF_MORE);
    }

    const char end_reply[] = "QUERY complete";
    SendTransaction(conn_info, end_reply, SafeStringLength(end_reply), CF_DONE);

    return true;
}
Esempio n. 7
0
static bool CFTestD_ProtocolError(
    ServerConnectionState *conn, const char *recvbuffer, char *sendbuffer)
{
    strcpy(sendbuffer, "BAD: Request denied");
    SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
    Log(LOG_LEVEL_INFO,
        "Closing connection due to illegal request: %s",
        recvbuffer);
    return false;
}
Esempio n. 8
0
static void ReplyNothing(ServerConnectionState *conn)
{
    char buffer[CF_BUFSIZE];

    snprintf(buffer, CF_BUFSIZE, "Hello %s (%s), nothing relevant to do here...\n\n", conn->hostname, conn->ipaddr);

    if (SendTransaction(&conn->conn_info, buffer, 0, CF_DONE) == -1)
    {
        Log(LOG_LEVEL_ERR, "Unable to send transaction. (send: %s)", GetErrorStr());
    }
}
Esempio n. 9
0
static void SendClassData(AgentConnection *conn)
{
    Rlist *classes, *rp;

    classes = RlistFromSplitRegex(SENDCLASSES, "[,: ]", 99, false);

    for (rp = classes; rp != NULL; rp = rp->next)
    {
        if (SendTransaction(conn->conn_info, RlistScalarValue(rp), 0, CF_DONE) == -1)
        {
            Log(LOG_LEVEL_ERR, "Transaction failed. (send: %s)", GetErrorStr());
            return;
        }
    }

    if (SendTransaction(conn->conn_info, CFD_TERMINATOR, 0, CF_DONE) == -1)
    {
        Log(LOG_LEVEL_ERR, "Transaction failed. (send: %s)", GetErrorStr());
        return;
    }
}
Esempio n. 10
0
static void AbortTransfer(ConnectionInfo *connection, char *filename)
{
    Log(LOG_LEVEL_VERBOSE, "Aborting transfer of file due to source changes");

    char sendbuffer[CF_BUFSIZE];
    snprintf(sendbuffer, CF_BUFSIZE, "%s%s: %s", CF_CHANGEDSTR1, CF_CHANGEDSTR2, filename);

    if (SendTransaction(connection, sendbuffer, 0, CF_DONE) == -1)
    {
        Log(LOG_LEVEL_VERBOSE, "Send failed in GetFile. (send: %s)", GetErrorStr());
    }
}
Esempio n. 11
0
static void FailedTransfer(ConnectionInfo *connection)
{
    Log(LOG_LEVEL_VERBOSE, "Transfer failure");

    char sendbuffer[CF_BUFSIZE];

    snprintf(sendbuffer, CF_BUFSIZE, "%s", CF_FAILEDSTR);

    if (SendTransaction(connection, sendbuffer, 0, CF_DONE) == -1)
    {
        Log(LOG_LEVEL_VERBOSE, "Send failed in GetFile. (send: %s)", GetErrorStr());
    }
}
Esempio n. 12
0
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;
    }
}
Esempio n. 13
0
void Terminate(ConnectionInfo *connection)
{
    char buffer[CF_BUFSIZE];

    memset(buffer, 0, CF_BUFSIZE);

    strcpy(buffer, CFD_TERMINATOR);

    if (SendTransaction(connection, buffer, strlen(buffer) + 1, CF_DONE) == -1)
    {
        Log(LOG_LEVEL_VERBOSE, "Unable to reply with terminator. (send: %s)", GetErrorStr());
    }
}
Esempio n. 14
0
void CompareLocalHash(ServerConnectionState *conn, char *sendbuffer, char *recvbuffer)
{
    unsigned char digest1[EVP_MAX_MD_SIZE + 1], digest2[EVP_MAX_MD_SIZE + 1];
    char filename[CF_BUFSIZE], rfilename[CF_BUFSIZE];
    char *sp;
    int i;

/* TODO - when safe change this proto string to sha2 */

    sscanf(recvbuffer, "MD5 %[^\n]", rfilename);

    sp = recvbuffer + strlen(recvbuffer) + CF_SMALL_OFFSET;

    for (i = 0; i < CF_DEFAULT_DIGEST_LEN; i++)
    {
        digest1[i] = *sp++;
    }

    memset(sendbuffer, 0, CF_BUFSIZE);

    TranslatePath(filename, rfilename);

    HashFile(filename, digest2, CF_DEFAULT_DIGEST);

    if ((HashesMatch(digest1, digest2, CF_DEFAULT_DIGEST)) || (HashesMatch(digest1, digest2, HASH_METHOD_MD5)))
    {
        sprintf(sendbuffer, "%s", CFD_FALSE);
        Log(LOG_LEVEL_DEBUG, "Hashes matched ok");
        SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
    }
    else
    {
        sprintf(sendbuffer, "%s", CFD_TRUE);
        Log(LOG_LEVEL_DEBUG, "Hashes didn't match");
        SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
    }
}
Esempio n. 15
0
static void SendClassData(AgentConnection *conn)
{
    Rlist *classes, *rp;
    char sendbuffer[CF_BUFSIZE];

    classes = RlistFromSplitRegex(SENDCLASSES, "[,: ]", 99, false);

    for (rp = classes; rp != NULL; rp = rp->next)
    {
        if (SendTransaction(&conn->conn_info, RlistScalarValue(rp), 0, CF_DONE) == -1)
        {
            Log(LOG_LEVEL_ERR, "Transaction failed. (send: %s)", GetErrorStr());
            return;
        }
    }

    snprintf(sendbuffer, CF_MAXVARSIZE, "%s", CFD_TERMINATOR);

    if (SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE) == -1)
    {
        Log(LOG_LEVEL_ERR, "Transaction failed. (send: %s)", GetErrorStr());
        return;
    }
}
Esempio n. 16
0
static void SendClassData(AgentConnection *conn)
{
    Rlist *classes, *rp;
    char sendbuffer[CF_BUFSIZE];

    classes = SplitRegexAsRList(SENDCLASSES, "[,: ]", 99, false);

    for (rp = classes; rp != NULL; rp = rp->next)
    {
        if (SendTransaction(conn->sd, rp->item, 0, CF_DONE) == -1)
        {
            CfOut(cf_error, "send", "Transaction failed");
            return;
        }
    }

    snprintf(sendbuffer, CF_MAXVARSIZE, "%s", CFD_TERMINATOR);

    if (SendTransaction(conn->sd, sendbuffer, 0, CF_DONE) == -1)
    {
        CfOut(cf_error, "send", "Transaction failed");
        return;
    }
}
Esempio n. 17
0
void CheckRemoteVersion()

{ char sendbuffer[CF_BUFSIZE];
  char recvbuffer[CF_BUFSIZE];
  int tosend;

Debug("CheckRemoteVersion\n");  
snprintf(sendbuffer,CF_BUFSIZE,"VERSION");
tosend = strlen(sendbuffer);

if (SendTransaction(CONN->sd,sendbuffer,tosend,CF_DONE) == -1)
   {
   snprintf(OUTPUT,CF_BUFSIZE*2,"Transmission failed while checking version");
   CfLog(cfinform,OUTPUT,"send");
   return;
   }

if (ReceiveTransaction(CONN->sd,recvbuffer,NULL) == -1)
   {
   snprintf(OUTPUT,CF_BUFSIZE*2,"Reply failed while checking version");
   CfLog(cfinform,OUTPUT,"send");
   CONN->protoversion = 0;
   return;
   }

if (BadProtoReply(recvbuffer))
   {
   CONN->protoversion = 0;
   return;
   }
else
   {
   CONN->protoversion = 1;
   return;
   }
}
Esempio n. 18
0
void DoExec(EvalContext *ctx, ServerConnectionState *conn, char *args)
{
    char ebuff[CF_EXPANDSIZE], line[CF_BUFSIZE], *sp;
    int print = false, i;
    FILE *pp;

    if ((CFSTARTTIME = time((time_t *) NULL)) == -1)
    {
        Log(LOG_LEVEL_ERR, "Couldn't read system clock. (time: %s)", GetErrorStr());
    }

    if (strlen(CFRUNCOMMAND) == 0)
    {
        Log(LOG_LEVEL_VERBOSE, "cf-serverd exec request: no cfruncommand defined");
        char sendbuffer[CF_BUFSIZE];
        strlcpy(sendbuffer, "Exec request: no cfruncommand defined\n", CF_BUFSIZE);
        SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
        return;
    }

    Log(LOG_LEVEL_VERBOSE, "Examining command string '%s'", args);

    for (sp = args; *sp != '\0'; sp++)  /* Blank out -K -f */
    {
        if ((*sp == ';') || (*sp == '&') || (*sp == '|'))
        {
            char sendbuffer[CF_BUFSIZE];
            snprintf(sendbuffer, CF_BUFSIZE, "You are not authorized to activate these classes/roles on host %s\n", VFQNAME);
            SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
            return;
        }

        if ((OptionFound(args, sp, "-K")) || (OptionFound(args, sp, "-f")))
        {
            *sp = ' ';
            *(sp + 1) = ' ';
        }
        else if (OptionFound(args, sp, "--no-lock"))
        {
            for (i = 0; i < strlen("--no-lock"); i++)
            {
                *(sp + i) = ' ';
            }
        }
        else if (OptionFound(args, sp, "--file"))
        {
            for (i = 0; i < strlen("--file"); i++)
            {
                *(sp + i) = ' ';
            }
        }
        else if ((OptionFound(args, sp, "--define")) || (OptionFound(args, sp, "-D")))
        {
            Log(LOG_LEVEL_VERBOSE, "Attempt to activate a predefined role..");

            if (!AuthorizeRoles(ctx, conn, sp))
            {
                char sendbuffer[CF_BUFSIZE];
                snprintf(sendbuffer, CF_BUFSIZE, "You are not authorized to activate these classes/roles on host %s\n", VFQNAME);
                SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
                return;
            }
        }
    }

    snprintf(ebuff, CF_BUFSIZE, "%s --inform", CFRUNCOMMAND);

    if (strlen(ebuff) + strlen(args) + 6 > CF_BUFSIZE)
    {
        char sendbuffer[CF_BUFSIZE];
        snprintf(sendbuffer, CF_BUFSIZE, "Command line too long with args: %s\n", ebuff);
        SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
        return;
    }
    else
    {
        if ((args != NULL) && (strlen(args) > 0))
        {
            char sendbuffer[CF_BUFSIZE];
            strcat(ebuff, " ");
            strncat(ebuff, args, CF_BUFSIZE - strlen(ebuff));
            snprintf(sendbuffer, CF_BUFSIZE, "cf-serverd Executing %s\n", ebuff);
            SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
        }
    }

    Log(LOG_LEVEL_INFO, "Executing command %s", ebuff);

    if ((pp = cf_popen_sh(ebuff, "r")) == NULL)
    {
        Log(LOG_LEVEL_ERR, "Couldn't open pipe to command '%s'. (pipe: %s)", ebuff, GetErrorStr());
        char sendbuffer[CF_BUFSIZE];
        snprintf(sendbuffer, CF_BUFSIZE, "Unable to run %s\n", ebuff);
        SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
        return;
    }

    for (;;)
    {
        ssize_t res = CfReadLine(line, CF_BUFSIZE, pp);

        if (res == 0)
        {
            break;
        }

        if (res == -1)
        {
            fflush(pp); /* FIXME: is it necessary? */
            break;
        }

        print = false;

        for (sp = line; *sp != '\0'; sp++)
        {
            if (!isspace((int) *sp))
            {
                print = true;
                break;
            }
        }

        if (print)
        {
            char sendbuffer[CF_BUFSIZE];
            snprintf(sendbuffer, CF_BUFSIZE, "%s\n", line);
            if (SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE) == -1)
            {
                Log(LOG_LEVEL_ERR, "Sending failed, aborting. (send: %s)", GetErrorStr());
                break;
            }
        }
    }

    cf_pclose(pp);
}
Esempio n. 19
0
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; 
}
Esempio n. 20
0
static void HailExec(AgentConnection *conn, char *peer, char *recvbuffer, char *sendbuffer)
{
    FILE *fp = stdout;
    char *sp;
    int n_read;

    if (strlen(DEFINECLASSES))
    {
        snprintf(sendbuffer, CF_BUFSIZE, "EXEC %s -D%s", REMOTE_AGENT_OPTIONS, DEFINECLASSES);
    }
    else
    {
        snprintf(sendbuffer, CF_BUFSIZE, "EXEC %s", REMOTE_AGENT_OPTIONS);
    }

    if (SendTransaction(conn->sd, sendbuffer, 0, CF_DONE) == -1)
    {
        CfOut(cf_error, "send", "Transmission rejected");
        DisconnectServer(conn);
        return;
    }

    fp = NewStream(peer);
    SendClassData(conn);

    while (true)
    {
        memset(recvbuffer, 0, CF_BUFSIZE);

        if ((n_read = ReceiveTransaction(conn->sd, recvbuffer, NULL)) == -1)
        {
            return;
        }

        if (n_read == 0)
        {
            break;
        }

        if (strlen(recvbuffer) == 0)
        {
            continue;
        }

        if ((sp = strstr(recvbuffer, CFD_TERMINATOR)) != NULL)
        {
            fprintf(fp, "%s> !!\n\n", VPREFIX);
            break;
        }

        if ((sp = strstr(recvbuffer, "BAD:")) != NULL)
        {
            fprintf(fp, "%s> !! %s\n", VPREFIX, recvbuffer + 4);
            continue;
        }

        if (strstr(recvbuffer, "too soon"))
        {
            fprintf(fp, "%s> !! %s\n", VPREFIX, recvbuffer);
            continue;
        }

        fprintf(fp, "%s> -> %s", VPREFIX, recvbuffer);
    }

    DeleteStream(fp);
    DisconnectServer(conn);
}
Esempio n. 21
0
File: server.c Progetto: tzz/core
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);
}
Esempio n. 22
0
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;
}
Esempio n. 23
0
static int AuthenticationDialogue(ServerConnectionState *conn, char *recvbuffer, int recvlen)
{
    char in[CF_BUFSIZE], *out, *decrypted_nonce;
    BIGNUM *counter_challenge = NULL;
    unsigned char digest[EVP_MAX_MD_SIZE + 1] = { 0 };
    unsigned int crypt_len, nonce_len = 0, encrypted_len = 0;
    char sauth[10], iscrypt = 'n', enterprise_field = 'c';
    int len_n = 0, len_e = 0, keylen, session_size;
    unsigned long err;
    RSA *newkey;
    int digestLen = 0;
    HashMethod digestType;

    if ((PRIVKEY == NULL) || (PUBKEY == NULL))
    {
        Log(LOG_LEVEL_ERR, "No public/private key pair exists, create one with cf-key");
        return false;
    }

    if (FIPS_MODE)
    {
        digestType = CF_DEFAULT_DIGEST;
        digestLen = CF_DEFAULT_DIGEST_LEN;
    }
    else
    {
        digestType = HASH_METHOD_MD5;
        digestLen = CF_MD5_LEN;
    }

/* proposition C1 */
/* Opening string is a challenge from the client (some agent) */

    sauth[0] = '\0';

    sscanf(recvbuffer, "%s %c %u %u %c", sauth, &iscrypt, &crypt_len, &nonce_len, &enterprise_field);

    if ((crypt_len == 0) || (nonce_len == 0) || (strlen(sauth) == 0))
    {
        Log(LOG_LEVEL_INFO, "Protocol format error in authentation from IP %s", conn->hostname);
        return false;
    }

    if (nonce_len > CF_NONCELEN * 2)
    {
        Log(LOG_LEVEL_INFO, "Protocol deviant authentication nonce from %s", conn->hostname);
        return false;
    }

    if (crypt_len > 2 * CF_NONCELEN)
    {
        Log(LOG_LEVEL_INFO, "Protocol abuse in unlikely cipher from %s", conn->hostname);
        return false;
    }

/* Check there is no attempt to read past the end of the received input */

    if (recvbuffer + CF_RSA_PROTO_OFFSET + nonce_len > recvbuffer + recvlen)
    {
        Log(LOG_LEVEL_INFO, "Protocol consistency error in authentication from %s", conn->hostname);
        return false;
    }

    if ((strcmp(sauth, "SAUTH") != 0) || (nonce_len == 0) || (crypt_len == 0))
    {
        Log(LOG_LEVEL_INFO, "Protocol error in RSA authentication from IP '%s'", conn->hostname);
        return false;
    }

    Log(LOG_LEVEL_DEBUG, "Challenge encryption = %c, nonce = %d, buf = %d", iscrypt, nonce_len, crypt_len);


    decrypted_nonce = xmalloc(crypt_len);

    if (iscrypt == 'y')
    {
        if (RSA_private_decrypt
            (crypt_len, recvbuffer + CF_RSA_PROTO_OFFSET, decrypted_nonce, PRIVKEY, RSA_PKCS1_PADDING) <= 0)
        {
            err = ERR_get_error();

            Log(LOG_LEVEL_ERR,
                "Private decrypt failed = '%s'. Probably the client has the wrong public key for this server",
                ERR_reason_error_string(err));
            free(decrypted_nonce);
            return false;
        }
    }
    else
    {
        if (nonce_len > crypt_len)
        {
            Log(LOG_LEVEL_ERR, "Illegal challenge");
            free(decrypted_nonce);
            return false;
        }

        memcpy(decrypted_nonce, recvbuffer + CF_RSA_PROTO_OFFSET, nonce_len);
    }

/* Client's ID is now established by key or trusted, reply with digest */

    HashString(decrypted_nonce, nonce_len, digest, digestType);

    free(decrypted_nonce);

/* Get the public key from the client */
    newkey = RSA_new();

/* proposition C2 */
    if ((len_n = ReceiveTransaction(conn->conn_info, recvbuffer, NULL)) == -1)
    {
        Log(LOG_LEVEL_INFO, "Protocol error 1 in RSA authentation from IP %s", conn->hostname);
        RSA_free(newkey);
        return false;
    }

    if (len_n == 0)
    {
        Log(LOG_LEVEL_INFO, "Protocol error 2 in RSA authentation from IP %s", conn->hostname);
        RSA_free(newkey);
        return false;
    }

    if ((newkey->n = BN_mpi2bn(recvbuffer, len_n, NULL)) == NULL)
    {
        err = ERR_get_error();
        Log(LOG_LEVEL_ERR, "Private decrypt failed = %s", ERR_reason_error_string(err));
        RSA_free(newkey);
        return false;
    }

/* proposition C3 */

    if ((len_e = ReceiveTransaction(conn->conn_info, recvbuffer, NULL)) == -1)
    {
        Log(LOG_LEVEL_INFO, "Protocol error 3 in RSA authentation from IP %s", conn->hostname);
        RSA_free(newkey);
        return false;
    }

    if (len_e == 0)
    {
        Log(LOG_LEVEL_INFO, "Protocol error 4 in RSA authentation from IP %s", conn->hostname);
        RSA_free(newkey);
        return false;
    }

    if ((newkey->e = BN_mpi2bn(recvbuffer, len_e, NULL)) == NULL)
    {
        err = ERR_get_error();
        Log(LOG_LEVEL_ERR, "Private decrypt failed = %s", ERR_reason_error_string(err));
        RSA_free(newkey);
        return false;
    }

    /* Compute and store hash of the client's public key. */
    Key *key = KeyNew(newkey, CF_DEFAULT_DIGEST);
    ConnectionInfoSetKey(conn->conn_info, key);
    Log(LOG_LEVEL_VERBOSE, "Public key identity of host '%s' is '%s'",
        conn->ipaddr, KeyPrintableHash(ConnectionInfoKey(conn->conn_info)));

    LastSaw1(conn->ipaddr, KeyPrintableHash(ConnectionInfoKey(conn->conn_info)),
             LAST_SEEN_ROLE_ACCEPT);

    if (!CheckStoreKey(conn, newkey))   /* conceals proposition S1 */
    {
        return false;
    }

/* Reply with digest of original challenge */

/* proposition S2 */

    SendTransaction(conn->conn_info, digest, digestLen, CF_DONE);

/* Send counter challenge to be sure this is a live session */

    counter_challenge = BN_new();
    if (counter_challenge == NULL)
    {
        Log(LOG_LEVEL_ERR, "Cannot allocate BIGNUM structure for counter challenge");
        return false;
    }

    BN_rand(counter_challenge, CF_NONCELEN, 0, 0);
    nonce_len = BN_bn2mpi(counter_challenge, in);

// hash the challenge from the client

    HashString(in, nonce_len, digest, digestType);

    encrypted_len = RSA_size(newkey);   /* encryption buffer is always the same size as n */

    out = xmalloc(encrypted_len + 1);

    if (RSA_public_encrypt(nonce_len, in, out, newkey, RSA_PKCS1_PADDING) <= 0)
    {
        err = ERR_get_error();
        Log(LOG_LEVEL_ERR, "Public encryption failed = %s", ERR_reason_error_string(err));
        free(out);
        return false;
    }

/* proposition S3 */
    SendTransaction(conn->conn_info, out, encrypted_len, CF_DONE);

/* if the client doesn't have our public key, send it */

    if (iscrypt != 'y')
    {
        /* proposition S4  - conditional */
        memset(in, 0, CF_BUFSIZE);
        len_n = BN_bn2mpi(PUBKEY->n, in);
        SendTransaction(conn->conn_info, in, len_n, CF_DONE);

        /* proposition S5  - conditional */
        memset(in, 0, CF_BUFSIZE);
        len_e = BN_bn2mpi(PUBKEY->e, in);
        SendTransaction(conn->conn_info, in, len_e, CF_DONE);
    }

/* Receive reply to counter_challenge */

/* proposition C4 */
    memset(in, 0, CF_BUFSIZE);

    if (ReceiveTransaction(conn->conn_info, in, NULL) == -1)
    {
        BN_free(counter_challenge);
        free(out);
        return false;
    }

    if (HashesMatch(digest, in, digestType))    /* replay / piggy in the middle attack ? */
    {
        Log(LOG_LEVEL_VERBOSE, "Authentication of client %s/%s achieved", conn->hostname, conn->ipaddr);
    }
    else
    {
        BN_free(counter_challenge);
        free(out);
        Log(LOG_LEVEL_INFO, "Challenge response from client %s was incorrect - ID false?", conn->ipaddr);
        return false;
    }

/* Receive random session key,... */

/* proposition C5 */

    memset(in, 0, CF_BUFSIZE);

    if ((keylen = ReceiveTransaction(conn->conn_info, in, NULL)) == -1)
    {
        BN_free(counter_challenge);
        free(out);
        return false;
    }

    if (keylen > CF_BUFSIZE / 2)
    {
        BN_free(counter_challenge);
        free(out);
        Log(LOG_LEVEL_INFO, "Session key length received from %s is too long", conn->ipaddr);
        return false;
    }

    session_size = CfSessionKeySize(enterprise_field);
    conn->session_key = xmalloc(session_size);
    conn->encryption_type = enterprise_field;

    Log(LOG_LEVEL_VERBOSE, "Receiving session key from client (size=%d)...", keylen);

    Log(LOG_LEVEL_DEBUG, "keylen = %d, session_size = %d", keylen, session_size);

    if (keylen == CF_BLOWFISHSIZE)      /* Support the old non-ecnrypted for upgrade */
    {
        memcpy(conn->session_key, in, session_size);
    }
    else
    {
        /* New protocol encrypted */

        if (RSA_private_decrypt(keylen, in, out, PRIVKEY, RSA_PKCS1_PADDING) <= 0)
        {
            err = ERR_get_error();
            Log(LOG_LEVEL_ERR, "Private decrypt failed = %s", ERR_reason_error_string(err));
            BN_free(counter_challenge);
            free(out);
            return false;
        }

        memcpy(conn->session_key, out, session_size);
    }

    BN_free(counter_challenge);
    free(out);
    return true;
}
Esempio n. 24
0
static void HailExec(AgentConnection *conn, char *peer, char *recvbuffer, char *sendbuffer)
{
    if (DEFINECLASSES[0] != '\0')
    {
        snprintf(sendbuffer, CF_BUFSIZE, "EXEC -D%s", DEFINECLASSES);
    }
    else
    {
        snprintf(sendbuffer, CF_BUFSIZE, "EXEC");
    }

    if (SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE) == -1)
    {
        Log(LOG_LEVEL_ERR, "Transmission rejected. (send: %s)", GetErrorStr());
        DisconnectServer(conn);
        return;
    }

    /* TODO we are sending class data right after EXEC, when the server might
     * have already rejected us with BAD reply. So this class data with the
     * CFD_TERMINATOR will be interpreted by the server as a new, bogus
     * protocol command, and the server will complain. */
    SendClassData(conn);

    FILE *fp = NewStream(peer);
    while (true)
    {
        memset(recvbuffer, 0, CF_BUFSIZE);

        int n_read = ReceiveTransaction(conn->conn_info, recvbuffer, NULL);

        if (n_read == -1)
        {
            break;
        }
        if (n_read == 0)                               /* connection closed */
        {
            break;
        }
        if (strncmp(recvbuffer, CFD_TERMINATOR, strlen(CFD_TERMINATOR)) == 0)
        {
            break;
        }

        const size_t recv_len = strlen(recvbuffer);
        const char   *ipaddr  = conn->remoteip;

        if (strncmp(recvbuffer, "BAD:", 4) == 0)
        {
            fprintf(fp, "%s> !! %s\n", ipaddr, recvbuffer + 4);
        }
        /* cf-serverd >= 3.7 quotes command output with "> ". */
        else if (strncmp(recvbuffer, "> ", 2) == 0)
        {
            fprintf(fp, "%s> -> %s", ipaddr, &recvbuffer[2]);
        }
        else
        {
            fprintf(fp, "%s> %s", ipaddr, recvbuffer);
        }

        if (recv_len > 0 && recvbuffer[recv_len - 1] != '\n')
        {
            /* We'll be printing double newlines here with new cf-serverd
             * versions, so check for already trailing newlines. */
            /* TODO deprecate this path in a couple of versions. cf-serverd is
             * supposed to munch the newlines so we must always append one. */
            fputc('\n', fp);
        }
    }

    if (fp != stdout)
    {
        fclose(fp);
    }
    DisconnectServer(conn);
}
Esempio n. 25
0
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;
}
Esempio n. 26
0
int IdentifyAgent(ConnectionInfo *conn_info)
{
    char uname[CF_BUFSIZE], sendbuff[CF_BUFSIZE];
    char dnsname[CF_MAXVARSIZE], localip[CF_MAX_IP_LEN];
    int ret;

    if ((!SKIPIDENTIFY) && (strcmp(VDOMAIN, CF_START_DOMAIN) == 0))
    {
        Log(LOG_LEVEL_ERR, "Undefined domain name");
        return false;
    }

    if (!SKIPIDENTIFY)
    {
        /* First we need to find out the IP address and DNS name of the socket
           we are sending from. This is not necessarily the same as VFQNAME if
           the machine has a different uname from its IP name (!) This can
           happen on poorly set up machines or on hosts with multiple
           interfaces, with different names on each interface ... */
        struct sockaddr_storage myaddr = {0};
        socklen_t myaddr_len = sizeof(myaddr);

        if (getsockname(conn_info->sd, (struct sockaddr *) &myaddr, &myaddr_len) == -1)
        {
            Log(LOG_LEVEL_ERR, "Couldn't get socket address. (getsockname: %s)", GetErrorStr());
            return false;
        }

        /* No lookup, just convert the bound address to string. */
        ret = getnameinfo((struct sockaddr *) &myaddr, myaddr_len,
                          localip, sizeof(localip),
                          NULL, 0, NI_NUMERICHOST);
        if (ret != 0)
        {
            Log(LOG_LEVEL_ERR,
                "During agent identification. (getnameinfo: %s)",
                  gai_strerror(ret));
            return false;
        }

        /* dnsname: Reverse lookup of the bound IP address. */
        ret = getnameinfo((struct sockaddr *) &myaddr, myaddr_len,
                          dnsname, sizeof(dnsname), NULL, 0, 0);
        if (ret != 0)
        {
            /* getnameinfo doesn't fail on resolution failure, it just prints
             * the IP, so here something else is wrong. */
            Log(LOG_LEVEL_ERR,
                "During agent identification for '%s'. (getnameinfo: %s)",
                  localip, gai_strerror(ret));
            return false;
        }

        /* getnameinfo() should always return FQDN. Some resolvers will not
         * return FQNAME and missing PTR will give numerical result */
        if ((strlen(VDOMAIN) > 0)                      /* TODO true always? */
            && (!IsIPV6Address(dnsname)) && (!strchr(dnsname, '.')))
        {
            strcat(dnsname, ".");
            strncat(dnsname, VDOMAIN, CF_MAXVARSIZE / 2);
        }

        /* Seems to be a bug in some resolvers that adds garbage, when it just
         * returns the input. */
        if (strncmp(dnsname, localip, strlen(localip)) == 0
            && dnsname[strlen(localip)] != '\0')
        {
            dnsname[strlen(localip)] = '\0';
            Log(LOG_LEVEL_WARNING,
                "getnameinfo() seems to append garbage to unresolvable IPs, bug mitigated by CFEngine but please report your platform!");
        }
    }
    else
    {
        assert(sizeof(localip) >= sizeof(VIPADDRESS));
        strcpy(localip, VIPADDRESS);

        Log(LOG_LEVEL_VERBOSE,
            "skipidentify was promised, so we are trusting and simply announcing the identity as '%s' for this host",
            strlen(VFQNAME) > 0 ? VFQNAME : "skipident");
        if (strlen(VFQNAME) > 0)
        {
            strcpy(dnsname, VFQNAME);
        }
        else
        {
            strcpy(dnsname, "skipident");
        }
    }

/* client always identifies as root on windows */
#ifdef __MINGW32__
    snprintf(uname, sizeof(uname), "%s", "root");
#else
    GetCurrentUserName(uname, sizeof(uname));
#endif

    snprintf(sendbuff, sizeof(sendbuff), "CAUTH %s %s %s %d",
             localip, dnsname, uname, 0);

    if (SendTransaction(conn_info, sendbuff, 0, CF_DONE) == -1)
    {
        Log(LOG_LEVEL_ERR,
              "During identify agent, could not send auth response. (SendTransaction: %s)", GetErrorStr());
        return false;
    }

    return true;
}
Esempio n. 27
0
int AuthenticateAgent(AgentConnection *conn, bool trust_key)
{
    char sendbuffer[CF_EXPANDSIZE], in[CF_BUFSIZE], *out, *decrypted_cchall;
    BIGNUM *nonce_challenge, *bn = NULL;
    unsigned char digest[EVP_MAX_MD_SIZE];
    int encrypted_len, nonce_len = 0, len, session_size;
    bool need_to_implicitly_trust_server;
    char enterprise_field = 'c';
    RSA *server_pubkey = NULL;

    if ((PUBKEY == NULL) || (PRIVKEY == NULL))
    {
        /* Try once more to load the keys, maybe the system is converging. */
        LoadSecretKeys();
        if ((PUBKEY == NULL) || (PRIVKEY == NULL))
        {
            char *pubkeyfile = PublicKeyFile(GetWorkDir());
            Log(LOG_LEVEL_ERR, "No public/private key pair found at: %s", pubkeyfile);
            free(pubkeyfile);
            return false;
        }
    }

    enterprise_field = CfEnterpriseOptions();
    session_size = CfSessionKeySize(enterprise_field);

/* Generate a random challenge to authenticate the server */

    nonce_challenge = BN_new();
    if (nonce_challenge == NULL)
    {
        Log(LOG_LEVEL_ERR, "Cannot allocate BIGNUM structure for server challenge");
        return false;
    }

    BN_rand(nonce_challenge, CF_NONCELEN, 0, 0);
    nonce_len = BN_bn2mpi(nonce_challenge, in);

    if (FIPS_MODE)
    {
        HashString(in, nonce_len, digest, CF_DEFAULT_DIGEST);
    }
    else
    {
        HashString(in, nonce_len, digest, HASH_METHOD_MD5);
    }

/* We assume that the server bound to the remote socket is the official one i.e. = root's */

    /* Ask the server to send us the public key if we don't have it. */
    if ((server_pubkey = HavePublicKeyByIP(conn->username, conn->remoteip)))
    {
        need_to_implicitly_trust_server = false;
        encrypted_len = RSA_size(server_pubkey);
    }
    else
    {
        need_to_implicitly_trust_server = true;
        encrypted_len = nonce_len;
    }

// Server pubkey is what we want to has as a unique ID

    snprintf(sendbuffer, sizeof(sendbuffer), "SAUTH %c %d %d %c",
             need_to_implicitly_trust_server ? 'n': 'y',
             encrypted_len, nonce_len, enterprise_field);

    out = xmalloc(encrypted_len);

    if (server_pubkey != NULL)
    {
        if (RSA_public_encrypt(nonce_len, in, out, server_pubkey, RSA_PKCS1_PADDING) <= 0)
        {
            Log(LOG_LEVEL_ERR,
                "Public encryption failed. (RSA_public_encrypt: %s)",
            CryptoLastErrorString());
            free(out);
            RSA_free(server_pubkey);
            return false;
        }

        memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, out, encrypted_len);
    }
    else
    {
        memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, in, nonce_len);
    }

/* proposition C1 - Send challenge / nonce */

    SendTransaction(conn->conn_info, sendbuffer, CF_RSA_PROTO_OFFSET + encrypted_len, CF_DONE);

    BN_free(bn);
    BN_free(nonce_challenge);
    free(out);

/*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->conn_info, 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->conn_info, sendbuffer, len, CF_DONE);

/* check reply about public key - server can break conn_info here */

/* proposition S1 */
    memset(in, 0, CF_BUFSIZE);

    if (ReceiveTransaction(conn->conn_info, in, NULL) == -1)
    {
        Log(LOG_LEVEL_ERR, "Protocol transaction broken off (1). (ReceiveTransaction: %s)", GetErrorStr());
        RSA_free(server_pubkey);
        return false;
    }

    if (BadProtoReply(in))
    {
        Log(LOG_LEVEL_ERR, "Bad protocol reply: %s", in);
        RSA_free(server_pubkey);
        return false;
    }

/* Get challenge response - should be CF_DEFAULT_DIGEST of challenge */

/* proposition S2 */
    memset(in, 0, CF_BUFSIZE);

    if (ReceiveTransaction(conn->conn_info, in, NULL) == -1)
    {
        Log(LOG_LEVEL_ERR, "Protocol transaction broken off (2). (ReceiveTransaction: %s)", GetErrorStr());
        RSA_free(server_pubkey);
        return false;
    }

    /* Check if challenge reply was correct */
    if ((HashesMatch(digest, in, CF_DEFAULT_DIGEST)) ||
        (HashesMatch(digest, in, HASH_METHOD_MD5)))  // Legacy
    {
        if (need_to_implicitly_trust_server == false)
        {
            /* The IP was found in lastseen. */
            Log(LOG_LEVEL_VERBOSE,
                ".....................[.h.a.i.l.].................................");
            Log(LOG_LEVEL_VERBOSE,
                "Strong authentication of server '%s' connection confirmed",
                conn->this_server);
        }
        else                                /* IP was not found in lastseen */
        {
            if (trust_key)
            {
                Log(LOG_LEVEL_VERBOSE,
                    "Trusting server identity, promise to accept key from '%s' = '%s'",
                    conn->this_server, conn->remoteip);
            }
            else
            {
                Log(LOG_LEVEL_ERR,
                    "Not authorized to trust public key of server '%s' (trustkey = false)",
                    conn->this_server);
                RSA_free(server_pubkey);
                return false;
            }
        }
    }
    else
    {
        Log(LOG_LEVEL_ERR, "Challenge response from server '%s/%s' was incorrect", conn->this_server,
             conn->remoteip);
        RSA_free(server_pubkey);
        return false;
    }

/* Receive counter challenge from server */

/* proposition S3 */
    memset(in, 0, CF_BUFSIZE);
    encrypted_len = ReceiveTransaction(conn->conn_info, in, NULL);

    if (encrypted_len <= 0)
    {
        Log(LOG_LEVEL_ERR, "Protocol transaction sent illegal cipher length");
        RSA_free(server_pubkey);
        return false;
    }

    decrypted_cchall = xmalloc(encrypted_len);

    if (RSA_private_decrypt(encrypted_len, in, decrypted_cchall, PRIVKEY, RSA_PKCS1_PADDING) <= 0)
    {
        Log(LOG_LEVEL_ERR,
            "Private decrypt failed, abandoning. (RSA_private_decrypt: %s)",
            CryptoLastErrorString());
        RSA_free(server_pubkey);
        return false;
    }

/* proposition C4 */
    if (FIPS_MODE)
    {
        HashString(decrypted_cchall, nonce_len, digest, CF_DEFAULT_DIGEST);
    }
    else
    {
        HashString(decrypted_cchall, nonce_len, digest, HASH_METHOD_MD5);
    }

    if (FIPS_MODE)
    {
        SendTransaction(conn->conn_info, digest, CF_DEFAULT_DIGEST_LEN, CF_DONE);
    }
    else
    {
        SendTransaction(conn->conn_info, digest, CF_MD5_LEN, CF_DONE);
    }

    free(decrypted_cchall);

/* If we don't have the server's public key, it will be sent */

    if (server_pubkey == NULL)
    {
        RSA *newkey = RSA_new();

        Log(LOG_LEVEL_VERBOSE, "Collecting public key from server!");

        /* proposition S4 - conditional */
        if ((len = ReceiveTransaction(conn->conn_info, in, NULL)) <= 0)
        {
            Log(LOG_LEVEL_ERR, "Protocol error in RSA authentation from IP '%s'", conn->this_server);
            return false;
        }

        if ((newkey->n = BN_mpi2bn(in, len, NULL)) == NULL)
        {
            Log(LOG_LEVEL_ERR,
                "Private key decrypt failed. (BN_mpi2bn: %s)",
                CryptoLastErrorString());
            RSA_free(newkey);
            return false;
        }

        /* proposition S5 - conditional */

        if ((len = ReceiveTransaction(conn->conn_info, in, NULL)) <= 0)
        {
            Log(LOG_LEVEL_INFO, "Protocol error in RSA authentation from IP '%s'",
                 conn->this_server);
            RSA_free(newkey);
            return false;
        }

        if ((newkey->e = BN_mpi2bn(in, len, NULL)) == NULL)
        {
            Log(LOG_LEVEL_ERR,
                "Public key decrypt failed. (BN_mpi2bn: %s)",
                CryptoLastErrorString());
            RSA_free(newkey);
            return false;
        }

        server_pubkey = RSAPublicKey_dup(newkey);
        RSA_free(newkey);
    }
    assert(server_pubkey != NULL);

/* proposition C5 */

    if (!SetSessionKey(conn))
    {
        Log(LOG_LEVEL_ERR, "Unable to set session key");
        return false;
    }

    if (conn->session_key == NULL)
    {
        Log(LOG_LEVEL_ERR, "A random session key could not be established");
        RSA_free(server_pubkey);
        return false;
    }

    encrypted_len = RSA_size(server_pubkey);

    out = xmalloc(encrypted_len);

    if (RSA_public_encrypt(session_size, conn->session_key, out, server_pubkey, RSA_PKCS1_PADDING) <= 0)
    {
        Log(LOG_LEVEL_ERR,
            "Public encryption failed. (RSA_public_encrypt: %s)",
            CryptoLastErrorString());
        free(out);
        RSA_free(server_pubkey);
        return false;
    }

    SendTransaction(conn->conn_info, out, encrypted_len, CF_DONE);

    Key *key = KeyNew(server_pubkey, CF_DEFAULT_DIGEST);
    conn->conn_info->remote_key = key;

    Log(LOG_LEVEL_VERBOSE, "Public key identity of host '%s' is: %s",
        conn->remoteip, KeyPrintableHash(conn->conn_info->remote_key));

    SavePublicKey(conn->username, KeyPrintableHash(conn->conn_info->remote_key), server_pubkey);

    unsigned int length = 0;
    LastSaw(conn->remoteip, KeyBinaryHash(conn->conn_info->remote_key, &length), LAST_SEEN_ROLE_CONNECT);

    free(out);

    return true;
}
Esempio n. 28
0
int IdentifyForVerification(int sd,char *localip,int family)

{ char sendbuff[CF_BUFSIZE],dnsname[CF_BUFSIZE];
  struct in_addr *iaddr;
  struct hostent *hp;
  int len,err;
  struct passwd *user_ptr;
  char *uname;
#if defined(HAVE_GETADDRINFO)
  char myaddr[256]; /* Compilation trick for systems that don't know ipv6 */
#else
  struct sockaddr_in myaddr;
#endif
  
memset(sendbuff,0,CF_BUFSIZE);
memset(dnsname,0,CF_BUFSIZE);

if (!SKIPIDENTIFY && (strcmp(VDOMAIN,CF_START_DOMAIN) == 0))
   {
   CfLog(cferror,"Undefined domain name","");
   return false;
   }

if (!SKIPIDENTIFY)
   {
/* First we need to find out the IP address and DNS name of the socket
   we are sending from. This is not necessarily the same as VFQNAME if
   the machine has a different uname from its IP name (!) This can
   happen on stupidly set up machines or on hosts with multiple
   interfaces, with different names on each interface ... */ 
   
   switch (family)
      {
      case AF_INET: len = sizeof(struct sockaddr_in);
          break;
#if defined(HAVE_GETADDRINFO)
      case AF_INET6: len = sizeof(struct sockaddr_in6);
          break;
#endif
      default:
          CfLog(cferror,"Software error in IdentifyForVerification","");
      }
   
   if (getsockname(sd,(struct sockaddr *)&myaddr,&len) == -1)
      {
      CfLog(cferror,"Couldn't get socket address\n","getsockname");
      return false;
      }
   
   snprintf(localip,CF_MAX_IP_LEN-1,"%s",sockaddr_ntop((struct sockaddr *)&myaddr)); 
   
   Debug("Identifying this agent as %s i.e. %s, with signature %d\n",localip,VFQNAME,CFSIGNATURE);
   
#if defined(HAVE_GETADDRINFO)
   
   if ((err=getnameinfo((struct sockaddr *)&myaddr,len,dnsname,CF_MAXVARSIZE,NULL,0,0)) != 0)
      {
      snprintf(OUTPUT,CF_BUFSIZE,"Couldn't look up address v6 for %s: %s\n",dnsname,gai_strerror(err));
      CfLog(cferror,OUTPUT,"");
      return false;
      }
   
#else 
   
   iaddr = &(myaddr.sin_addr); 

   hp = gethostbyaddr((void *)iaddr,sizeof(myaddr.sin_addr),family);
   
   if ((hp == NULL) || (hp->h_name == NULL))
      {
      CfLog(cferror,"Couldn't lookup IP address\n","gethostbyaddr");
      return false;
      }
   
   strncpy(dnsname,hp->h_name,CF_MAXVARSIZE);
   
   if ((strstr(hp->h_name,".") == 0) && (strlen(VDOMAIN) > 0))
      {
      strcat(dnsname,".");
      strcat(dnsname,VDOMAIN);
      } 
#endif 
   }
else
   {
   strcpy(localip,VIPADDRESS);
   
   if (strlen(VFQNAME) > 0)
      {
      Verbose("SkipIdent was requested, so we are trusting and annoucning the identity as (%s) for this host\n",VFQNAME);
      strcat(dnsname,VFQNAME);
      }
   else
      {
      strcat(dnsname,"skipident");
      }
   }

user_ptr = getpwuid(getuid());
uname = user_ptr ? user_ptr->pw_name : "UNKNOWN";

/* Some resolvers will not return FQNAME and missing PTR will give numerical result */

if ((strlen(VDOMAIN) > 0) && !IsIPV6Address(dnsname) && !strchr(dnsname,'.'))
   {
   Debug("Appending domain %s to %s\n",VDOMAIN,dnsname);
   strcat(dnsname,".");
   strncat(dnsname,VDOMAIN,CF_MAXVARSIZE/2);
   }  

if (strncmp(dnsname,localip,strlen(localip)) == 0)
   {
   /* Seems to be a bug in some resolvers that adds garbage, when it just returns the input */
   strcpy(dnsname,localip);
   }

if (strlen(dnsname) == 0)
   {
   strcpy(dnsname,localip);
   }

snprintf(sendbuff,CF_BUFSIZE-1,"CAUTH %s %s %s %d",localip,dnsname,uname,CFSIGNATURE);

Debug("SENT:::%s\n",sendbuff);
 
SendTransaction(sd,sendbuff,0,CF_DONE);
return true;
}
Esempio n. 29
0
void CfEncryptGetFile(ServerFileGetState *args)
/* Because the stream doesn't end for each file, we need to know the
   exact number of bytes transmitted, which might change during
   encryption, hence we need to handle this with transactions */
{
    int fd, n_read, cipherlen, finlen;
    off_t total = 0, count = 0;
    char sendbuffer[CF_BUFSIZE + 256], out[CF_BUFSIZE], filename[CF_BUFSIZE];
    unsigned char iv[32] =
        { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
    int blocksize = CF_BUFSIZE - 4 * CF_INBAND_OFFSET;
    EVP_CIPHER_CTX ctx;
    char *key, enctype;
    struct stat sb;
    ConnectionInfo *conn_info = &args->connect->conn_info;

    key = (args->connect)->session_key;
    enctype = (args->connect)->encryption_type;

    TranslatePath(filename, args->replyfile);

    stat(filename, &sb);

    Log(LOG_LEVEL_DEBUG, "CfEncryptGetFile('%s'), size = %" PRIdMAX, filename, (intmax_t) sb.st_size);

/* Now check to see if we have remote permission */

    if (!TransferRights(filename, args, &sb))
    {
        RefuseAccess(args->connect, args->buf_size, "");
        FailedTransfer(conn_info);
    }

    EVP_CIPHER_CTX_init(&ctx);

    if ((fd = open(filename, O_RDONLY)) == -1)
    {
        Log(LOG_LEVEL_ERR, "Open error of file '%s'. (open: %s)", filename, GetErrorStr());
        FailedTransfer(conn_info);
    }
    else
    {
        int div = 3;

        if (sb.st_size > 10485760L) /* File larger than 10 MB, checks every 64kB */
        {
            div = 32;
        }

        while (true)
        {
            memset(sendbuffer, 0, CF_BUFSIZE);

            if ((n_read = read(fd, sendbuffer, blocksize)) == -1)
            {
                Log(LOG_LEVEL_ERR, "Read failed in EncryptGetFile. (read: %s)", GetErrorStr());
                break;
            }

            off_t savedlen = sb.st_size;

            if (count++ % div == 0)       /* Don't do this too often */
            {
                Log(LOG_LEVEL_DEBUG, "Restatting '%s' - size %d", filename, n_read);
                if (stat(filename, &sb))
                {
                    Log(LOG_LEVEL_ERR, "Cannot stat file '%s' (stat: %s)",
                            filename, GetErrorStr());
                    break;
                }
            }

            if (sb.st_size != savedlen)
            {
                AbortTransfer(conn_info, filename);
                break;
            }

            total += n_read;

            if (n_read > 0)
            {
                EVP_EncryptInit_ex(&ctx, CfengineCipher(enctype), NULL, key, iv);

                if (!EVP_EncryptUpdate(&ctx, out, &cipherlen, sendbuffer, n_read))
                {
                    FailedTransfer(conn_info);
                    EVP_CIPHER_CTX_cleanup(&ctx);
                    close(fd);
                    return;
                }

                if (!EVP_EncryptFinal_ex(&ctx, out + cipherlen, &finlen))
                {
                    FailedTransfer(conn_info);
                    EVP_CIPHER_CTX_cleanup(&ctx);
                    close(fd);
                    return;
                }
            }

            if (total >= savedlen)
            {
                if (SendTransaction(conn_info, out, cipherlen + finlen, CF_DONE) == -1)
                {
                    Log(LOG_LEVEL_VERBOSE, "Send failed in GetFile. (send: %s)", GetErrorStr());
                    EVP_CIPHER_CTX_cleanup(&ctx);
                    close(fd);
                    return;
                }
                break;
            }
            else
            {
                if (SendTransaction(conn_info, out, cipherlen + finlen, CF_MORE) == -1)
                {
                    Log(LOG_LEVEL_VERBOSE, "Send failed in GetFile. (send: %s)", GetErrorStr());
                    close(fd);
                    EVP_CIPHER_CTX_cleanup(&ctx);
                    return;
                }
            }
        }
    }

    EVP_CIPHER_CTX_cleanup(&ctx);
    close(fd);
}
Esempio n. 30
0
int StatFile(ServerConnectionState *conn, char *sendbuffer, char *ofilename)
/* Because we do not know the size or structure of remote datatypes,*/
/* the simplest way to transfer the data is to convert them into */
/* plain text and interpret them on the other side. */
{
    Stat cfst;
    struct stat statbuf, statlinkbuf;
    char linkbuf[CF_BUFSIZE], filename[CF_BUFSIZE];
    int islink = false;

    TranslatePath(filename, ofilename);

    memset(&cfst, 0, sizeof(Stat));

    if (strlen(ReadLastNode(filename)) > CF_MAXLINKSIZE)
    {
        snprintf(sendbuffer, CF_BUFSIZE, "BAD: Filename suspiciously long [%s]\n", filename);
        Log(LOG_LEVEL_ERR, "%s", sendbuffer);
        SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
        return -1;
    }

    if (lstat(filename, &statbuf) == -1)
    {
        snprintf(sendbuffer, CF_BUFSIZE, "BAD: unable to stat file %s", filename);
        Log(LOG_LEVEL_VERBOSE, "%s. (lstat: %s)", sendbuffer, GetErrorStr());
        SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
        return -1;
    }

    cfst.cf_readlink = NULL;
    cfst.cf_lmode = 0;
    cfst.cf_nlink = CF_NOSIZE;

    memset(linkbuf, 0, CF_BUFSIZE);

#ifndef __MINGW32__                   // windows doesn't support symbolic links
    if (S_ISLNK(statbuf.st_mode))
    {
        islink = true;
        cfst.cf_type = FILE_TYPE_LINK; /* pointless - overwritten */
        cfst.cf_lmode = statbuf.st_mode & 07777;
        cfst.cf_nlink = statbuf.st_nlink;

        if (readlink(filename, linkbuf, CF_BUFSIZE - 1) == -1)
        {
            sprintf(sendbuffer, "BAD: unable to read link\n");
            Log(LOG_LEVEL_ERR, "%s. (readlink: %s)", sendbuffer, GetErrorStr());
            SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
            return -1;
        }

        Log(LOG_LEVEL_DEBUG, "readlink '%s'", linkbuf);

        cfst.cf_readlink = linkbuf;
    }
#endif /* !__MINGW32__ */

    if ((!islink) && (stat(filename, &statbuf) == -1))
    {
        Log(LOG_LEVEL_VERBOSE, "BAD: unable to stat file '%s'. (stat: %s)",
            filename, GetErrorStr());
        SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
        return -1;
    }

    Log(LOG_LEVEL_DEBUG, "Getting size of link deref '%s'", linkbuf);

    if (islink && (stat(filename, &statlinkbuf) != -1))       /* linktype=copy used by agent */
    {
        statbuf.st_size = statlinkbuf.st_size;
        statbuf.st_mode = statlinkbuf.st_mode;
        statbuf.st_uid = statlinkbuf.st_uid;
        statbuf.st_gid = statlinkbuf.st_gid;
        statbuf.st_mtime = statlinkbuf.st_mtime;
        statbuf.st_ctime = statlinkbuf.st_ctime;
    }

    if (S_ISDIR(statbuf.st_mode))
    {
        cfst.cf_type = FILE_TYPE_DIR;
    }

    if (S_ISREG(statbuf.st_mode))
    {
        cfst.cf_type = FILE_TYPE_REGULAR;
    }

    if (S_ISSOCK(statbuf.st_mode))
    {
        cfst.cf_type = FILE_TYPE_SOCK;
    }

    if (S_ISCHR(statbuf.st_mode))
    {
        cfst.cf_type = FILE_TYPE_CHAR_;
    }

    if (S_ISBLK(statbuf.st_mode))
    {
        cfst.cf_type = FILE_TYPE_BLOCK;
    }

    if (S_ISFIFO(statbuf.st_mode))
    {
        cfst.cf_type = FILE_TYPE_FIFO;
    }

    cfst.cf_mode = statbuf.st_mode & 07777;
    cfst.cf_uid = statbuf.st_uid & 0xFFFFFFFF;
    cfst.cf_gid = statbuf.st_gid & 0xFFFFFFFF;
    cfst.cf_size = statbuf.st_size;
    cfst.cf_atime = statbuf.st_atime;
    cfst.cf_mtime = statbuf.st_mtime;
    cfst.cf_ctime = statbuf.st_ctime;
    cfst.cf_ino = statbuf.st_ino;
    cfst.cf_dev = statbuf.st_dev;
    cfst.cf_readlink = linkbuf;

    if (cfst.cf_nlink == CF_NOSIZE)
    {
        cfst.cf_nlink = statbuf.st_nlink;
    }

#if !defined(__MINGW32__)
    if (statbuf.st_size > statbuf.st_blocks * DEV_BSIZE)
#else
# ifdef HAVE_ST_BLOCKS
    if (statbuf.st_size > statbuf.st_blocks * DEV_BSIZE)
# else
    if (statbuf.st_size > ST_NBLOCKS(statbuf) * DEV_BSIZE)
# endif
#endif
    {
        cfst.cf_makeholes = 1;  /* must have a hole to get checksum right */
    }
    else
    {
        cfst.cf_makeholes = 0;
    }

    memset(sendbuffer, 0, CF_BUFSIZE);

    /* send as plain text */

    Log(LOG_LEVEL_DEBUG, "OK: type = %d, mode = %" PRIoMAX ", lmode = %" PRIoMAX ", uid = %" PRIuMAX ", gid = %" PRIuMAX ", size = %" PRIdMAX ", atime=%" PRIdMAX ", mtime = %" PRIdMAX,
            cfst.cf_type, (uintmax_t)cfst.cf_mode, (uintmax_t)cfst.cf_lmode, (intmax_t)cfst.cf_uid, (intmax_t)cfst.cf_gid, (intmax_t) cfst.cf_size,
            (intmax_t) cfst.cf_atime, (intmax_t) cfst.cf_mtime);

    snprintf(sendbuffer, CF_BUFSIZE, "OK: %d %ju %ju %ju %ju %jd %jd %jd %jd %d %d %d %jd",
             cfst.cf_type, (uintmax_t)cfst.cf_mode, (uintmax_t)cfst.cf_lmode,
             (uintmax_t)cfst.cf_uid, (uintmax_t)cfst.cf_gid, (intmax_t)cfst.cf_size,
             (intmax_t) cfst.cf_atime, (intmax_t) cfst.cf_mtime, (intmax_t) cfst.cf_ctime,
             cfst.cf_makeholes, cfst.cf_ino, cfst.cf_nlink, (intmax_t) cfst.cf_dev);

    SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);

    memset(sendbuffer, 0, CF_BUFSIZE);

    if (cfst.cf_readlink != NULL)
    {
        strcpy(sendbuffer, "OK:");
        strcat(sendbuffer, cfst.cf_readlink);
    }
    else
    {
        sprintf(sendbuffer, "OK:");
    }

    SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
    return 0;
}