Beispiel #1
0
/*****************************************************************************************
* Authorization routines
*****************************************************************************************/
int ossl_verify_cb (int ok, X509_STORE_CTX *ctx)
{
    int cert_error = X509_STORE_CTX_get_error(ctx);
    X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx);

    EST_LOG_INFO("enter function: ok=%d cert_error=%d", ok, cert_error);

    if (!ok) {
        if (current_cert) {
            X509_NAME_print_ex_fp(stdout,
                                  X509_get_subject_name(current_cert),
                                  0, XN_FLAG_ONELINE);
            printf("\n");
        }
        EST_LOG_INFO("%serror %d at %d depth lookup: %s",
                     X509_STORE_CTX_get0_parent_ctx(ctx) ? "[CRL path]" : "",
                     cert_error,
                     X509_STORE_CTX_get_error_depth(ctx),
                     X509_verify_cert_error_string(cert_error));
        switch (cert_error) {
        case X509_V_ERR_UNABLE_TO_GET_CRL:
            /*
             * We've enabled CRL checking in the TLS stack.  If
             * the application hasn't loaded a CRL, then this
             * verify error can occur.  The peer's cert is valid,
             * but we can't confirm if it was revoked.  We'll
             * warn the application.
             */
            EST_LOG_WARN("No CRL loaded, TLS peer will be allowed.");
            ok = 1;
            break;
        case X509_V_ERR_NO_EXPLICIT_POLICY:
        case X509_V_ERR_CERT_HAS_EXPIRED:

        /* since we are just checking the certificates, it is
         * ok if they are self signed. But we should still warn
         * the user.
         */

        case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
        /* Continue after extension errors too */
        case X509_V_ERR_INVALID_CA:
        case X509_V_ERR_INVALID_NON_CA:
        case X509_V_ERR_PATH_LENGTH_EXCEEDED:
        case X509_V_ERR_INVALID_PURPOSE:
        case X509_V_ERR_CRL_HAS_EXPIRED:
        case X509_V_ERR_CRL_NOT_YET_VALID:
        case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
        case X509_V_ERR_CERT_REVOKED:
        default:
            EST_LOG_WARN("Certificate verify failed (reason=%d)",
                         cert_error);
            break;
        }
        return ok;
    }
    return (ok);
}
Beispiel #2
0
static HTTP_HEADER * parse_http_headers (unsigned char **buf, int *num_headers)
{
    int i;
    HTTP_HEADER *hdrs;
    char *hdr_end;
    errno_t safec_rc;

    *num_headers = 0;
    hdrs = malloc(sizeof(HTTP_HEADER) * MAX_HEADERS);
    if (!hdrs) {
        EST_LOG_ERR("malloc failure");
        return (NULL);
    }

    /*
     * Find offset of header deliminter
     */
    safec_rc = strstr_s((char *) *buf, strnlen_s((char *) *buf, RSIZE_MAX_STR),
            "\r\n\r\n", MAX_HEADER_DELIMITER_LEN, &hdr_end);

    if (safec_rc != EOK) {
        EST_LOG_INFO("strstr_s error 0x%xO\n", safec_rc);
    }

    /*
     * Skip the first line
     */
    skip((char **)buf, "\r\n");

    for (i = 0; i < MAX_HEADERS; i++) {
        hdrs[i].name = skip_quoted((char **)buf, ":", " ", 0);
        hdrs[i].value = skip((char **)buf, "\r\n");
        fflush(stdout);
        EST_LOG_INFO("Found HTTP header -> %s:%s", hdrs[i].name, hdrs[i].value);
        fflush(stdout);
        if (hdrs[i].name[0] == '\0') {
            break;
        }
        *num_headers = i + 1;
        if ((*buf) > (unsigned char *)hdr_end) {
            break;
        }
    }
    EST_LOG_INFO("Found %d HTTP headers\n", *num_headers);
    return (hdrs);
}
Beispiel #3
0
/*
 * This function is used to load an X509_STORE using raw
 * data from a buffer.  The data is expected to be PEM
 * encoded.
 *
 * Returns the number of certs added to the store
 */
static int ossl_init_cert_store_from_raw (X509_STORE *store,
                                           unsigned char *raw, int size)
{
    STACK_OF(X509_INFO) * sk = NULL;
    X509_INFO *xi;
    BIO *in;
    int cert_cnt = 0;

    in = BIO_new_mem_buf(raw, size);
    if (in == NULL) {
        EST_LOG_ERR("Unable to open the raw CA cert buffer");
        return 0;
    }

    /* This loads from a file, a stack of x509/crl/pkey sets */
    sk = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL);
    if (sk == NULL) {
        EST_LOG_ERR("Unable to read PEM encoded certs from BIO");
        BIO_free(in);
        return 0;
    }
    BIO_free(in);

    /* scan over it and pull out the CRL's */
    while (sk_X509_INFO_num(sk)) {
        xi = sk_X509_INFO_shift(sk);
        if (xi->x509 != NULL) {
            EST_LOG_INFO("Adding cert to store (%s)", xi->x509->name);
            X509_STORE_add_cert(store, xi->x509);
	    cert_cnt++;
        }
        if (xi->crl != NULL) {
            EST_LOG_INFO("Adding CRL to store");
            X509_STORE_add_crl(store, xi->crl);
        }
        X509_INFO_free(xi);
    }

    if (sk != NULL) {
        sk_X509_INFO_pop_free(sk, X509_INFO_free);
    }
    return (cert_cnt);
}
Beispiel #4
0
static HTTP_HEADER * parse_http_headers (unsigned char **buf, int *num_headers)
{
    int i;
    HTTP_HEADER *hdrs;
    char *hdr_end;

    *num_headers = 0;
    hdrs = malloc(sizeof(HTTP_HEADER) * MAX_HEADERS);
    if (!hdrs) {
        EST_LOG_ERR("malloc failure");
        return (NULL);
    }

    /*
     * Find offset of header delimiter
     */
    hdr_end = strstr((const char *)*buf, "\r\n\r\n");

    /*
     * Skip the first line
     */
    skip((char **)buf, "\r\n");

    for (i = 0; i < MAX_HEADERS; i++) {
        hdrs[i].name = skip_quoted((char **)buf, ":", " ", 0);
        hdrs[i].value = skip((char **)buf, "\r\n");
        EST_LOG_INFO("Found HTTP header -> %s: %s", hdrs[i].name, hdrs[i].value);
        if (hdrs[i].name[0] == '\0') {
            break;
        }
        *num_headers = i + 1;
        if ((*buf) > (unsigned char *)hdr_end) {
            break;
        }
    }
    EST_LOG_INFO("Found %d HTTP headers", *num_headers);
    return (hdrs);
}
Beispiel #5
0
static int est_strcasecmp_s (char *s1, char *s2)
{
    errno_t safec_rc;
    int diff;
    
    safec_rc = strcasecmp_s(s1, strnlen_s(s1, RSIZE_MAX_STR), s2, &diff);

    if (safec_rc != EOK) {
    	/*
    	 * Log that we encountered a SafeC error
     	 */
     	EST_LOG_INFO("strcasecmp_s error 0x%xO\n", safec_rc);
    } 

    return diff;
}
Beispiel #6
0
/*
 * This routine will check the result code from an enroll
 * attempt and propagate the retry-after message to the 
 * client if needed.
 */
static EST_ERROR est_proxy_propagate_retry (EST_CTX *ctx, void *http_ctx)
{
    /*
     * The CA did not sign the request and has asked the
     * client to retry in the future.  This may occur if
     * the CA is not configured for automatic enrollment.
     * Send the HTTP retry response to the client.
     * We need to propagate the retry-after response to
     * the client.
     */
    EST_LOG_INFO("CA server requests retry, propagate this to the client (%d)", 
        ctx->retry_after_delay);
    if (EST_ERR_NONE != est_server_send_http_retry_after(ctx, http_ctx, ctx->retry_after_delay)) {
        EST_LOG_ERR("HTTP write error while propagating retry-after");
        return (EST_ERR_HTTP_WRITE);
    }
    return (EST_ERR_NONE);
}
Beispiel #7
0
/*
 * This function should be called by the web server layer when
 * a HTTP request arrives on the listening port of the EST proxy.
 * It will determine the EST request type and dispatch the request
 * to the appropriate handler.
 *
 * Paramters:
 *      ctx:	    Pointer to EST_CTX
 *      http_ctx:   Context pointer from web server
 *      method:     The HTML method in the request, should be either "GET" or "POST"
 *	uri:	    pointer to HTTP URI
 *	body:	    pointer to full HTML body contents
 *	body_len:   length of HTML body
 *	ct:         HTML content type header
 */
EST_ERROR est_proxy_http_request (EST_CTX *ctx, void *http_ctx,
                                  char *method, char *uri,
                                  char *body, int body_len, const char *ct)
{
    SSL *ssl;
    EST_ERROR rc;

    if (!ctx) {
        return (EST_ERR_NO_CTX);
    }

    /*
     * Verify the context is for a proxy, not a client or server
     */
    if (ctx->est_mode != EST_PROXY) {
        return (EST_ERR_BAD_MODE);
    }

    EST_LOG_INFO("Proxy started handling %s %s", method, uri);

    /*
     * See if this is a cacerts request
     */
    if (strncmp(uri, EST_CACERTS_URI, EST_URI_MAX_LEN) == 0) {
        /* Only GET is allowed */
        if (strcmp(method, "GET")) {
	    rc = EST_ERR_WRONG_METHOD;
        } else {
	    rc = est_handle_cacerts(ctx, http_ctx);
	}
    }

    /*
     * See if this is a simple (re-)enrollment request
     */
    else if (strncmp(uri, EST_SIMPLE_ENROLL_URI, EST_URI_MAX_LEN) == 0
          || strncmp(uri, EST_RE_ENROLL_URI, EST_URI_MAX_LEN) == 0) {
        /* Only POST is allowed */
        if (strcmp(method, "POST")) {
            rc = EST_ERR_WRONG_METHOD;
        }
	else if (!ct) {
            EST_LOG_WARN("Incoming HTTP header has no Content-Type header");
	    rc = EST_ERR_BAD_CONTENT_TYPE;
	} else {
	    /*
	     * Get the SSL context, which is required for authenticating the client.
	     */
	    ssl = (SSL*)mg_get_conn_ssl(http_ctx);
	    if (!ssl) {
		rc = EST_ERR_NO_SSL_CTX;
	    } else {
		rc = est_proxy_handle_simple_enroll(ctx, http_ctx, ssl, ct, body, body_len, 
						    strncmp(uri, EST_RE_ENROLL_URI, EST_URI_MAX_LEN) == 0);
		if (rc != EST_ERR_NONE && rc != EST_ERR_AUTH_PENDING && rc != EST_ERR_AUTH_FAIL) {
#if 0 // I see no reason for hiding real cause of error from clients
		    rc = EST_ERR_BAD_PKCS10;
#endif
		}
	    }
	}
    }

#if 0
    /*
     * See if this is a keygen request
     * FIXME: this is currently not implemented
     */
    else if (strncmp(uri, EST_KEYGEN_URI, EST_URI_MAX_LEN) == 0) {

        /* Only POST is allowed */
        if (strcmp(method, "POST")) {
            rc = EST_ERR_WRONG_METHOD;
        }
	else if (!ct) {
            EST_LOG_WARN("Incoming HTTP header has no Content-Type header");
	    rc = EST_ERR_BAD_CONTENT_TYPE;
	}
        else if (est_proxy_handle_keygen(ctx)) {
            rc = EST_ERR_HTTP_WRITE;             //FIXME: need the appropriate return code
        }
    }
#endif

    /*
     * See if this is a CSR attributes request
     */
    else if (strncmp(uri, EST_CSR_ATTRS_URI, EST_URI_MAX_LEN) == 0) {
        /* Only GET is allowed */
        if (strcmp(method, "GET")) {
            rc = EST_ERR_WRONG_METHOD;
        } else {
           rc = est_proxy_handle_csr_attrs(ctx, http_ctx);
        }
    }

    /*
     * Send a 404 error if the URI didn't match 
     */
    else {
        rc = EST_ERR_HTTP_NOT_FOUND;
    }

    EST_LOG_INFO("Proxy finished handling %s %s with rv = %d (%s)", method, uri, rc, EST_ERR_NUM_TO_STR(rc));

    if (rc != EST_ERR_NONE) {
	est_send_http_error(ctx, http_ctx, rc);
    }
    return (rc);
}
Beispiel #8
0
/*
 * This function is used by the server side of the EST proxy to respond to an
 * incoming CSR Attributes request.  This function is similar to the Client API
 * function, est_client_get_csrattrs().
  */
static int est_proxy_handle_csr_attrs (EST_CTX *ctx, void *http_ctx)
{
    int rv = EST_ERR_NONE;
    int pop_present;
    char *csr_data, *csr_data_pop;
    int csr_len, csr_pop_len;
    EST_CTX *client_ctx;

    /*
     * get the client context for this thread
     */
    client_ctx = get_client_ctx(ctx);
    if (!client_ctx) {
        EST_LOG_ERR("Unable to obtain client context for proxy operation");
	return (EST_ERR_NO_CTX);
    }

    /*
     * Invoke client code to retrieve the CSR attributes.
     * Note: there is no need to authenticate the client (see sec 4.5)
     */
    EST_LOG_INFO("Proxy get csr attributes");
    rv = est_client_get_csrattrs(client_ctx, (unsigned char **)&csr_data, &csr_len);
    /*
     * csr_data points to the memory allocated to hold the csr attributes,
     * which will be freed in this call stack.  To prevent a double-free
     * we null the to pointer on the client context.
     */
    client_ctx->retrieved_csrattrs = NULL;
    client_ctx->retrieved_csrattrs_len = 0;
    if (rv == EST_ERR_NONE) {
	ctx->csr_pop_present = 0;
	if (ctx->server_enable_pop) {
	    rv = est_is_challengePassword_present(csr_data, csr_len, &pop_present);
	    if (rv != EST_ERR_NONE) {
		EST_LOG_ERR("Error during PoP/sanity check");
		est_send_http_error(ctx, http_ctx, EST_ERR_HTTP_NO_CONTENT);
		return (EST_ERR_NONE);
	    }
	    ctx->csr_pop_present = pop_present;

	    if (!ctx->csr_pop_present) {
		if (csr_len == 0) {
                    csr_data = malloc(EST_CSRATTRS_POP_LEN + 1);
		    if (!csr_data) {
			return (EST_ERR_MALLOC);
		    }
		    strncpy(csr_data, EST_CSRATTRS_POP, EST_CSRATTRS_POP_LEN);
		    csr_data[EST_CSRATTRS_POP_LEN] = 0;
		    csr_len = EST_CSRATTRS_POP_LEN;
		    return (est_send_csrattr_data(ctx, csr_data, csr_len, http_ctx));
		}
		rv = est_add_challengePassword(csr_data, csr_len, &csr_data_pop, &csr_pop_len);
		if (rv != EST_ERR_NONE) {
		    if (csr_data) {
		        free(csr_data);
		    }
		    EST_LOG_ERR("Error during add PoP");
		    est_send_http_error(ctx, http_ctx, EST_ERR_HTTP_NO_CONTENT);
		    return (EST_ERR_NONE);
		}
		if (csr_data) {
		    free(csr_data);
		}
		csr_data = csr_data_pop;
		csr_len = csr_pop_len;
	    }
	}
    } else {
	EST_LOG_ERR("Server not reachable or sent corrupt attributes");
	est_send_http_error(ctx, http_ctx, EST_ERR_HTTP_NO_CONTENT);
	return (EST_ERR_NONE);
    }
    return (est_send_csrattr_data(ctx, csr_data, csr_len, http_ctx));
}
Beispiel #9
0
/*
 * This function is used by the server side of the EST proxy to respond to an
 * incoming Simple Enroll request.  This function is similar to the Client API
 * function, est_client_enroll_req(), except it bypasses some things that are
 * not done when functioning as a proxy, such as signing the CSR, not
 * inserting the TLS unique id and instead including the id-kp-cmcRA usage
 * extension.
 */
static EST_ERROR est_proxy_handle_simple_enroll (EST_CTX *ctx, void *http_ctx,
                                                 SSL *ssl, const char *ct,
                                                 char *body, int body_len,
					         int reenroll)
{
    EST_ERROR rv;
    BUF_MEM *pkcs10;
    unsigned char *pkcs7;
    int pkcs7_len = 0;
    X509_REQ *csr = NULL;
    EST_CTX *client_ctx;
     
    /*
     * Make sure the client has sent us a PKCS10 CSR request
     */
    if (strcmp(ct, "application/pkcs10")) {
        return (EST_ERR_BAD_CONTENT_TYPE);
    }

    /*
     * Authenticate the client
     */
    switch (est_enroll_auth(ctx, http_ctx, ssl, reenroll)) {
    case EST_HTTP_AUTH:
    case EST_SRP_AUTH:
    case EST_CERT_AUTH:
        break;
    case EST_HTTP_AUTH_PENDING:
        return (EST_ERR_AUTH_PENDING);
        break;
    case EST_UNAUTHORIZED:
    default:
        return (EST_ERR_AUTH_FAIL);
        break;
    }

    /*
     * Parse the PKCS10 CSR from the client
     */
    csr = est_server_parse_csr((unsigned char*)body, body_len);
    if (!csr) {
	EST_LOG_ERR("Unable to parse the PKCS10 CSR sent by the client");
	return (EST_ERR_BAD_PKCS10);
    }
    
    /*
     * Perform a sanity check on the CSR
     */
    if (est_server_check_csr(csr)) {
	EST_LOG_ERR("PKCS10 CSR sent by the client failed sanity check");
	X509_REQ_free(csr);
	return (EST_ERR_BAD_PKCS10);
    }

    /*
     * Do the PoP check (Proof of Possession).  The challenge password
     * in the pkcs10 request should match the TLS unique ID.
     */
    rv = est_tls_uid_auth(ctx, ssl, csr);
    X509_REQ_free(csr);

    if (rv != EST_ERR_NONE) {
        return (EST_ERR_AUTH_FAIL_TLSUID);
    }

    /*
     * body now points to the pkcs10 data, pass
     * this to the enrollment routine.  Need to hi-jack
     * a BUF_MEM.  Attach the body to a new BUF_MEM
     */
    pkcs10 = BUF_MEM_new();
    pkcs10->data = body;
    pkcs10->length = body_len;
    pkcs10->max = body_len;

    /*
     * get the client context for this thread
     */
    client_ctx = get_client_ctx(ctx);
    if (!client_ctx) {
        EST_LOG_ERR("Unable to obtain client context for proxy operation");
        est_proxy_free_ossl_bufmem(pkcs10);
	return (EST_ERR_NO_CTX);
    }

    /*
     * Allocate some space to hold the cert that we
     * expect to receive from the EST server.
     */
    pkcs7 = malloc(EST_CA_MAX); 

    /*
     * Attempt to enroll the CSR from the client
     */
    rv = est_proxy_send_enroll_request(client_ctx, pkcs10, pkcs7, &pkcs7_len, reenroll);

    /*
     * Handle any errors that likely occurred
     */
    switch (rv) {
    case EST_ERR_AUTH_FAIL:
        /* Try one more time if we're doing Digest auth */
        if ((ctx->auth_mode == AUTH_DIGEST ||
             ctx->auth_mode == AUTH_BASIC  ||
             ctx->auth_mode == AUTH_TOKEN)) {
            
            EST_LOG_INFO("HTTP Auth failed, trying again with digest/basic parameters");

            rv = est_proxy_send_enroll_request(client_ctx, pkcs10, pkcs7, &pkcs7_len, reenroll);
	    if (rv == EST_ERR_CA_ENROLL_RETRY) {
	        rv = est_proxy_propagate_retry(client_ctx, http_ctx);
	    } else if (rv != EST_ERR_NONE) {
                EST_LOG_WARN("EST enrollment failed, error code is %d", rv);
            }
        }
        break;
    case EST_ERR_CA_ENROLL_RETRY:
	rv = est_proxy_propagate_retry(client_ctx, http_ctx);
	break;
    default:
        EST_LOG_WARN("Initial EST enrollment request error code is %d", rv);
	break;
    }

    client_ctx->auth_mode = AUTH_NONE;
    
    /*
     * Prevent OpenSSL from freeing our data
     */
    est_proxy_free_ossl_bufmem(pkcs10);

    /*
     * If we have a cert response from the EST server, let's forward
     * it back to the EST client
     */
    if (pkcs7_len > 0) {
        rv = est_proxy_propagate_pkcs7(http_ctx, pkcs7, pkcs7_len);
    }
    free(pkcs7);

    return (rv);
}
Beispiel #10
0
/*
 * This function takes in the list of headers that were in the server's
 * response, it walks through the headers looking for a Retry-After response
 * header.  If one is found, the value is parsed and saved away in the EST
 * context.  This value can be in one of two formats, both are represented as
 * an ASCII string.  The first format can be a count of the number of seconds
 * the client should wait before retrying the request.  The second format is a
 * time/date stamp of the point in time at which the client should retry the
 * request.  The result of this function is the setting of the retry_after
 * values in the context.  If no retry-after header was received, or was
 * received and could not be parsed, the values will be zero, otherwise, they
 * are set to the value received.
 *
 * NOTE: The EST client currently does not support the time/date format
 * response and will not process a response in this format.
 */
static EST_ERROR est_io_parse_http_retry_after_resp (EST_CTX *ctx,
                                                     HTTP_HEADER *hdrs,
                                                     int hdr_cnt)
{
    EST_ERROR rv = EST_ERR_INVALID_RETRY_VALUE;
    int i;
    int cmp_result;
    int rc;
    long long int temp_ll;
    int found = 0;
    
    /*
     * Initialize assuming there was no retry-after header.
     */
    ctx->retry_after_delay = 0;
    ctx->retry_after_date = 0;
    
    for (i = 0; i < hdr_cnt; i++) {
        
        cmp_result = strncasecmp(hdrs[i].name, EST_HTTP_HDR_RETRY_AFTER, 
		sizeof(EST_HTTP_HDR_RETRY_AFTER));
        if (!cmp_result) {
            
            EST_LOG_INFO("Retry-After value = %s", hdrs[i].value);
            found = 1;
            /*
             * Determine whether or not the value is a date/time string
             * or is an integer representing the number of seconds
             * that the client must wait.
             */
            if (isalpha((int)*(hdrs[i].value))) {
#ifdef RETRY_AFTER_DELAY_TIME_SUPPORT
                int rc;
                /*
                 * Convert the date/time string into a time_t
                 */
                rc = parsedate(hdrs[i].value, &ctx->retry_after_date);
                if (rc != PARSEDATE_OK) {
                    EST_LOG_ERR("Retry-After value could not be parsed");
                }
#else
                /*
                 * This format is not currently supported.
                 */
                EST_LOG_ERR("Retry-After value not in the correct format");
#endif                
            } else {
                /*
                 * make sure it's all digits, make sure it's no larger than a
                 * four byte integer, and cache away the value returned for
                 * the retry delay.
                 */
                rc = strisdigit(hdrs[i].value, 10); // max of 10 decimal places
                if (rc) {
                    temp_ll = atoll(hdrs[i].value);
                    if (temp_ll <= INT_MAX) {
                        ctx->retry_after_delay = (int) temp_ll;
                        rv = EST_ERR_CA_ENROLL_RETRY;
                    } else {
                        EST_LOG_ERR("Retry-After value too large");
                    }
                    
                } else {
                    EST_LOG_ERR("Retry-After value could not be parsed");
                }
            }
        }
    }
    if (found == 0) {
        EST_LOG_ERR("Retry-After header missing");
    }    
    return rv;
}
Beispiel #11
0
/*
 * This function provides the primary entry point into
 * this module.  It's used by the EST client to read the
 * HTTP response from the server.  The data is read from
 * the SSL context and HTTP parsing is invoked.
 *
 * If EST_ERR_NONE is returned then the raw_buf buffer must
 * be freed by the caller, otherwise, it is freed here.
 */
EST_ERROR est_io_get_response (EST_CTX *ctx, SSL *ssl, EST_OPERATION op,
                               unsigned char **buf, int *payload_len)
{
    int rv = EST_ERR_NONE;
    HTTP_HEADER *hdrs;
    int hdr_cnt;
    int http_status;
    unsigned char *raw_buf, *payload_buf, *payload;    
    int raw_len = 0;
    

    raw_buf = malloc(EST_CA_MAX);
    if (raw_buf == NULL) {
        EST_LOG_ERR("Unable to allocate memory");
        return EST_ERR_MALLOC;
    }
    memset(raw_buf, 0, EST_CA_MAX);
    payload = raw_buf;
    
    /*
     * Read the raw data from the SSL connection
     */
    rv = est_io_read_raw(ssl, raw_buf, EST_CA_MAX, &raw_len, ctx->read_timeout);
    if (rv != EST_ERR_NONE) {
        EST_LOG_INFO("No valid response to process");
        free(raw_buf);
        return (rv);
    }
    if (raw_len <= 0) {
        EST_LOG_WARN("Received empty HTTP response from server");
        free(raw_buf);
        return (EST_ERR_HTTP_NOT_FOUND);
    }
    EST_LOG_INFO("Read %d bytes of HTTP data", raw_len);
    
    /*
     * Parse the HTTP header to get the status
     * Look for status 200 for success
     */
    http_status = est_io_parse_response_status_code(raw_buf);
    hdrs = parse_http_headers(&payload, &hdr_cnt);
    EST_LOG_INFO("HTTP status %d received", http_status);

    /*
     * Check the Status header first to see
     * if the server accepted our request.
     */
    switch (http_status) {
    case 200:
        /* Server reported OK, nothing to do */
        break;
    case 204:
    case 404:
        EST_LOG_ERR("Server responded with 204/404, no content or not found");
        if (op == EST_GET_CSRATTRS) {
	    rv = EST_ERR_NONE;
        } else if (http_status == 404) {
            rv = EST_ERR_HTTP_NOT_FOUND;            
        } else {
            rv = EST_ERR_HTTP_NO_CONTENT;
        }
        break;
    case 202:
        /* Server is asking for a retry */
        EST_LOG_INFO("EST server responded with retry-after");
        rv = est_io_parse_http_retry_after_resp(ctx, hdrs, hdr_cnt);
        break;
    case 400:
        EST_LOG_ERR("HTTP response from EST server was BAD REQUEST");
        rv = EST_ERR_HTTP_BAD_REQ;
	break;
    case 401:
        /* Server is requesting user auth credentials */
        EST_LOG_INFO("EST server requesting user authentication");

        /* Check if we've already tried authenticating, if so, then bail
         * First time through, auth_mode will be set to NONE
         */
        if (ctx->auth_mode == AUTH_DIGEST ||
            ctx->auth_mode == AUTH_BASIC ||
            ctx->auth_mode == AUTH_TOKEN) {
            ctx->auth_mode = AUTH_FAIL;
            rv = EST_ERR_AUTH_FAIL;
            break;
        }
        est_io_parse_http_auth_request(ctx, hdrs, hdr_cnt);
        rv = EST_ERR_AUTH_FAIL;
        break;
    case -1:
        /* Unsupported HTTP response */
        EST_LOG_ERR("Unsupported HTTP response from EST server (%d)", http_status);
        rv = EST_ERR_UNKNOWN;
        break;
    default:
        /* Some other HTTP response was given, do we want to handle these? */
        EST_LOG_ERR("HTTP response from EST server was %d", http_status);
        rv = EST_ERR_HTTP_UNSUPPORTED;
        break;
    }

    if (rv == EST_ERR_NONE) {
        /*
         * Get the Content-Type and Content-Length headers
         * and verify the HTTP response contains the correct amount
         * of data.
         */
        *payload_len = est_io_check_http_hdrs(hdrs, hdr_cnt, op);
	if (*payload_len < 0) {
	    rv = -*payload_len;
	} else {
	    EST_LOG_INFO("HTTP Content len=%d", *payload_len);
	}

        if (*payload_len > EST_CA_MAX) {
            EST_LOG_ERR("Content Length larger than maximum value of %d.",
                        EST_CA_MAX);
            rv = EST_ERR_UNKNOWN;
            *payload_len = 0;
            *buf = NULL;
        } else if (*payload_len <= 0) {
            *payload_len = 0;
            *buf = NULL;
        } else {
            /*
             * Allocate the buffer to hold the payload to be passed back
             */
            payload_buf = malloc(*payload_len);   
            if (!payload_buf) {
                EST_LOG_ERR("Unable to allocate memory");
                free(raw_buf);
                free(hdrs);
                return EST_ERR_MALLOC;
            }
            memcpy(payload_buf, payload, *payload_len);
            *buf = payload_buf;
        }
    }
    
    if (raw_buf) {
        free(raw_buf);
    }
    if (hdrs) {
        free(hdrs);
    }
    return (rv);
}
Beispiel #12
0
/*
 * This function parses the authentication tokens from
 * the server when the server is requesting HTTP digest
 * authentication.  The tokens are required to generate
 * a valid authentication response in future HTTP
 * requests.
 */
static EST_ERROR est_io_parse_auth_tokens (EST_CTX *ctx, char *hdr)
{
    int rv = EST_ERR_NONE;
    char *p = hdr;
    char *token = NULL;
    char *value = NULL;
    int diff;
    errno_t safec_rc;

    /*
     * header will come in with the basic or digest field still on the front.
     * skip over it.
     */

    token = HTNextField(&p);

    while ((token = HTNextField(&p))) {
        if (!est_strcasecmp_s(token, "realm")) {
            if ((value = HTNextField(&p))) {
                if (EOK != strncpy_s(ctx->realm, MAX_REALM, value, MAX_REALM)) {
                    rv = EST_ERR_INVALID_TOKEN;
                }
            } else {
                rv = EST_ERR_INVALID_TOKEN;
            }
        } else if (!est_strcasecmp_s(token, "nonce")) {
            if ((value = HTNextField(&p))) {
                if (EOK != strncpy_s(ctx->s_nonce, MAX_NONCE, value, MAX_NONCE)) {
                    rv = EST_ERR_INVALID_TOKEN;
                }                
            } else {
                rv = EST_ERR_INVALID_TOKEN;
            }
        } else if (!est_strcasecmp_s(token, "qop")) {
            if ((value = HTNextField(&p))) {

                if (value[0] == '\0') {
                    EST_LOG_WARN("Unsupported qop value: %s", value);
                } else {
                    safec_rc = memcmp_s(value, sizeof("auth"), "auth", sizeof("auth"), &diff);
                    if (safec_rc != EOK) {
                        EST_LOG_INFO("memcmp_s error 0x%xO\n", safec_rc);
                    }
                    if (diff && (safec_rc == EOK)) {
                        EST_LOG_WARN("Unsupported qop value: %s", value);
                    }
                }
            } else {
                rv = EST_ERR_INVALID_TOKEN;
            }
        } else if (!est_strcasecmp_s(token, "algorithm")) {
            if ((value = HTNextField(&p)) && est_strcasecmp_s(value, "md5")) {
                EST_LOG_ERR("Unsupported digest algorithm: %s", value);
                /*
                 **  We only support MD5 for the moment
                 */
                rv = EST_ERR_INVALID_TOKEN;
            }
        } else if (!est_strcasecmp_s(token, "error")) {
            if ((value = HTNextField(&p))) {
                if (EOK != strncpy_s(ctx->token_error, MAX_TOKEN_ERROR, value, MAX_TOKEN_ERROR)) {
                    rv = EST_ERR_INVALID_TOKEN;
                }
            } else {
                rv = EST_ERR_INVALID_TOKEN;
            }
        } else if (!est_strcasecmp_s(token, "error_description")) {
            if ((value = HTNextField(&p))) {
                if (EOK != strncpy_s(ctx->token_error_desc, MAX_TOKEN_ERROR_DESC, value, MAX_TOKEN_ERROR_DESC)) {
                    rv = EST_ERR_INVALID_TOKEN;
                }
            } else {
                rv = EST_ERR_INVALID_TOKEN;
            }
        } else {
            EST_LOG_WARN("Unsupported auth token ignored: %s", token);
        }

        if (rv == EST_ERR_INVALID_TOKEN) {
            memzero_s(ctx->s_nonce, MAX_NONCE+1);
            break;
        }   
    }
    return (rv);
}