Beispiel #1
0
/**
 * @return >0: the version that was negotiated
 *          0: no agreement on version was reached
 *         -1: error
 */
int ServerNegotiateProtocol(const ConnectionInfo *conn_info)
{
    int ret;
    char input[CF_SMALLBUF] = "";

    /* Send "CFE_v%d cf-serverd version". */
    char version_string[CF_MAXVARSIZE];
    int len = snprintf(version_string, sizeof(version_string),
                       "CFE_v%d cf-serverd %s\n",
                       SERVER_PROTOCOL_VERSION, VERSION);

    ret = TLSSend(conn_info->ssl, version_string, len);
    if (ret != len)
    {
        Log(LOG_LEVEL_ERR, "Connection was hung up!");
        return -1;
    }

    /* Receive CFE_v%d ... */
    ret = TLSRecvLines(conn_info->ssl, input, sizeof(input));
    if (ret <= 0)
    {
        Log(LOG_LEVEL_ERR,
            "Client closed connection early! He probably does not trust our key...");
        return -1;
    }

    int version_received = -1;
    ret = sscanf(input, "CFE_v%d", &version_received);
    if (ret != 1)
    {
        Log(LOG_LEVEL_ERR,
            "Protocol version negotiation failed! Received: %s",
            input);
        return -1;
    }

    /* For now we support only one version, so just check they match... */
    if (version_received == SERVER_PROTOCOL_VERSION)
    {
        char s[] = "OK\n";
        TLSSend(conn_info->ssl, s, sizeof(s)-1);
        return version_received;
    }
    else
    {
        char s[] = "BAD unsupported protocol version\n";
        TLSSend(conn_info->ssl, s, sizeof(s)-1);
        Log(LOG_LEVEL_ERR,
            "Client advertises unsupported protocol version: %d", version_received);
        return 0;
    }
}
Beispiel #2
0
/**
 * @brief Return the client's username into the #username variable
 * @TODO More protocol identity: IDENTIFY USERNAME=xxx HOSTNAME=xxx CUSTOMNAME=xxx
 * @return 1 if IDENTITY command was parsed correctly. Identity fields
 *         (only #username for now) have the respective string values, or they are
 *         empty if field was not on IDENTITY line.
 * @return -1 in case of error.
 */
int ServerIdentifyClient(const ConnectionInfo *conn_info,
                         char *username, size_t username_size)
{
    char line[1024], word1[1024], word2[1024];
    int line_pos = 0, chars_read = 0;
    int ret;

    /* Reset all identity variables, we'll set them later according to fields
     * on IDENTITY line. For now only "username" setting exists... */
    username[0] = '\0';

    ret = TLSRecvLines(conn_info->ssl, line, sizeof(line));
    if (ret <= 0)
    {
        return -1;
    }

    /* Assert sscanf() is safe to use. */
    assert(sizeof(word1)>=sizeof(line) && sizeof(word2)>=sizeof(line));

    ret = sscanf(line, "IDENTITY %[^=]=%s %n", word1, word2, &chars_read);
    while (ret >= 2)
    {
        /* Found USERNAME identity setting */
        if (strcmp(word1, "USERNAME") == 0)
        {
            if (strlen(word2) < username_size)
            {
                strcpy(username, word2);
            }
            else
            {
                Log(LOG_LEVEL_ERR, "IDENTITY parameter too long: %s=%s",
                    word1, word2);
                return -1;
            }
            Log(LOG_LEVEL_VERBOSE, "Setting IDENTITY: %s=%s",
                word1, word2);
        }
        else
        {
            Log(LOG_LEVEL_VERBOSE, "Received unknown IDENTITY parameter: %s=%s",
                word1, word2);
        }

        line_pos += chars_read;
        ret = sscanf(&line[line_pos], " %[^=]=%s %n", word1, word2, &chars_read);
    }

    return 1;
}
Beispiel #3
0
/**
 * 1. Receive "CFE_v%d" server hello
 * 2. Send two lines: one "CFE_v%d" with the protocol version we wish to have,
 *    and another with id, e.g. "IDENTITY USERNAME=blah".
 * 3. Receive "OK WELCOME"
 *
 * @return > 0: success. #conn_info->type has been updated with the negotiated
 *              protocol version.
 *           0: server denial
 *          -1: error
 */
int TLSClientIdentificationDialog(ConnectionInfo *conn_info,
                                  const char *username)
{
    char line[1024] = "";
    int ret;

    /* Receive CFE_v%d ... That's the first thing the server sends. */
    ret = TLSRecvLines(conn_info->ssl, line, sizeof(line));

    ProtocolVersion wanted_version;
    if (conn_info->protocol == CF_PROTOCOL_UNDEFINED)
    {
        /* TODO parse CFE_v%d received and use that version if it's lower. */
        wanted_version = CF_PROTOCOL_LATEST;
    }
    else
    {
        wanted_version = conn_info->protocol;
    }

    /* Send "CFE_v%d cf-agent version". */
    char version_string[128];
    int len = snprintf(version_string, sizeof(version_string),
                       "CFE_v%d %s %s\n",
                       wanted_version, "cf-agent", VERSION); /* TODO argv[0] */

    ret = TLSSend(conn_info->ssl, version_string, len);
    if (ret != len)
    {
        Log(LOG_LEVEL_ERR, "Connection was hung up during identification!");
        return -1;
    }

    strcpy(line, "IDENTITY");
    size_t line_len = strlen(line);

    if (username != NULL)
    {
        ret = snprintf(&line[line_len], sizeof(line) - line_len,
                       " USERNAME=%s", username);
        if (ret >= sizeof(line) - line_len)
        {
            Log(LOG_LEVEL_ERR, "Sending IDENTITY truncated: %s", line);
            return -1;
        }
        line_len += ret;
    }

    /* Overwrite the terminating '\0', we don't need it anyway. */
    line[line_len] = '\n';
    line_len++;

    ret = TLSSend(conn_info->ssl, line, line_len);
    if (ret == -1)
    {
        Log(LOG_LEVEL_ERR,
            "Connection was hung up during identification! (2)");
        return -1;
    }

    /* Server might hang up here, after we sent identification! We
     * must get the "OK WELCOME" message for everything to be OK. */
    static const char OK[] = "OK WELCOME";
    size_t OK_len = sizeof(OK) - 1;
    ret = TLSRecvLines(conn_info->ssl, line, sizeof(line));
    if (ret == -1)
    {
        Log(LOG_LEVEL_ERR,
            "Connection was hung up during identification! (3)");
        return -1;
    }

    if (ret < OK_len || strncmp(line, OK, OK_len) != 0)
    {
        Log(LOG_LEVEL_ERR,
            "Peer did not accept our identity! Responded: %s",
            line);
        return 0;
    }

    /* Before it contained the protocol version we requested from the server,
     * now we put in the value that was negotiated. */
    conn_info->protocol = wanted_version;

    return 1;
}
Beispiel #4
0
/**
 * 1. Send "CFE_v%d" server hello.
 * 2. Receive two lines: One "CFE_v%d" with the protocol version the client
 *    wishes to have, and one "IDENTITY USERNAME=blah ..." with identification
 *    information for the client.
 *
 * @note For Identification dialog to end successfully, one "OK WELCOME" line
 *       must be sent right after this function, after identity is verified.
 *
 * @TODO More protocol identity. E.g.
 *       IDENTITY USERNAME=xxx HOSTNAME=xxx CUSTOMNAME=xxx
 *
 * @retval true if protocol version was successfully negotiated and IDENTITY
 *         command was parsed correctly. Identity fields (only #username for
 *         now) have the respective string values, or they are empty if field
 *         was not on IDENTITY line.  #conn_info->type has been updated with
 *         the negotiated protocol version.
 * @retval false in case of error.
 */
static bool ServerIdentificationDialog(ConnectionInfo *conn_info,
                                       char *username, size_t username_size)
{
    int ret;
    char input[1024] = "";
    /* The only protocol version we support inside TLS, for now. */
    const int SERVER_PROTOCOL_VERSION = CF_PROTOCOL_LATEST;

    /* Send "CFE_v%d cf-serverd version". */
    char version_string[CF_MAXVARSIZE];
    int len = snprintf(version_string, sizeof(version_string),
                       "CFE_v%d cf-serverd %s\n",
                       SERVER_PROTOCOL_VERSION, VERSION);

    ret = TLSSend(conn_info->ssl, version_string, len);
    if (ret != len)
    {
        Log(LOG_LEVEL_NOTICE, "Connection was hung up!");
        return false;
    }

    /* Receive CFE_v%d ... \n IDENTITY USERNAME=... */
    int input_len = TLSRecvLines(conn_info->ssl, input, sizeof(input));
    if (input_len <= 0)
    {
        Log(LOG_LEVEL_NOTICE,
            "Client closed connection early! He probably does not trust our key...");
        return false;
    }

    int version_received = -1;
    ret = sscanf(input, "CFE_v%d", &version_received);
    if (ret != 1)
    {
        Log(LOG_LEVEL_NOTICE,
            "Protocol version negotiation failed! Received: %s",
            input);
        return false;
    }

    /* For now we support only one version inside TLS. */
    /* TODO value should not be hardcoded but compared to enum ProtocolVersion. */
    if (version_received != SERVER_PROTOCOL_VERSION)
    {
        Log(LOG_LEVEL_NOTICE,
            "Client advertises disallowed protocol version: %d",
            version_received);
        return false;
        /* TODO send "BAD ..." ? */
    }

    /* Did we receive 2nd line or do we need to receive again? */
    const char id_line[] = "\nIDENTITY ";
    char *line2 = memmem(input, input_len, id_line, strlen(id_line));
    if (line2 == NULL)
    {
        /* Wait for 2nd line to arrive. */
        input_len = TLSRecvLines(conn_info->ssl, input, sizeof(input));
        if (input_len <= 0)
        {
            Log(LOG_LEVEL_NOTICE,
                "Client closed connection during identification dialog!");
            return false;
        }
        line2 = input;
    }
    else
    {
        line2++;                                               /* skip '\n' */
    }

    /***** Parse all IDENTITY fields from line2 *****/

    char word1[1024], word2[1024];
    int line2_pos = 0, chars_read = 0;

    /* Reset all identity variables, we'll set them according to fields
     * on IDENTITY line. For now only "username" setting exists... */
    username[0] = '\0';

    /* Assert sscanf() is safe to use. */
    assert(sizeof(word1)>=sizeof(input) && sizeof(word2)>=sizeof(input));

    ret = sscanf(line2, "IDENTITY %[^=]=%s%n", word1, word2, &chars_read);
    while (ret >= 2)
    {
        /* Found USERNAME identity setting */
        if (strcmp(word1, "USERNAME") == 0)
        {
            if ((strlen(word2) < username_size) && (IsUserNameValid(word2) == true))
            {
                strcpy(username, word2);
            }
            else
            {
                Log(LOG_LEVEL_NOTICE, "Received invalid IDENTITY: %s=%s",
                    word1, word2);
                return false;
            }
            Log(LOG_LEVEL_VERBOSE, "Setting IDENTITY: %s=%s",
                word1, word2);
        }
        /* ... else if (strcmp()) for other acceptable IDENTITY parameters. */
        else
        {
            Log(LOG_LEVEL_VERBOSE, "Received unknown IDENTITY parameter: %s=%s",
                word1, word2);
        }

        line2_pos += chars_read;
        ret = sscanf(&line2[line2_pos], " %[^=]=%s%n", word1, word2, &chars_read);
    }

    /* Version client and server agreed on. */
    conn_info->type = version_received;

    return true;
}