예제 #1
0
파일: est_ossl_util.c 프로젝트: DDvO/libest
/*****************************************************************************************
* 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);
}
예제 #2
0
/*
 * This function can be used to output the OpenSSL
 * error buffer.  This is useful when an OpenSSL
 * API call fails and you'd like to provide some
 * detail to the user regarding the cause of the
 * failure.
 */
void ossl_dump_ssl_errors ()
{
    BIO		*e = NULL;
    BUF_MEM	*bptr = NULL;

    e = BIO_new(BIO_s_mem());
    if (!e) {
	EST_LOG_ERR("BIO_new failed");
	return;
    }
    ERR_print_errors(e);
    (void)BIO_flush(e);
    BIO_get_mem_ptr(e, &bptr);
    EST_LOG_WARN("OSSL error: %s", bptr->data); 
    BIO_free_all(e);
}
예제 #3
0
파일: est_proxy.c 프로젝트: DDvO/libest
/*
 * 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);
}
예제 #4
0
파일: est_proxy.c 프로젝트: DDvO/libest
/*
 * 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);
}
예제 #5
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;

    /*
     * 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 (!strcasecmp(token, "realm")) {
            if ((value = HTNextField(&p))) {
                strncpy(ctx->realm, value, MAX_REALM);
            } else {
                rv = EST_ERR_INVALID_TOKEN;
            }
        } else if (!strcasecmp(token, "nonce")) {
            if ((value = HTNextField(&p))) {
                strncpy(ctx->s_nonce, value, MAX_NONCE);
            } else {
                rv = EST_ERR_INVALID_TOKEN;
            }
        } else if (!strcasecmp(token, "qop")) {
            if ((value = HTNextField(&p))) {
                if (strcmp(value, "auth")) {
                    EST_LOG_WARN("Unsupported qop value: %s", value);
                }
            } else {
                rv = EST_ERR_INVALID_TOKEN;
            }
        } else if (!strcasecmp(token, "algorithm")) {
            if ((value = HTNextField(&p)) && strcasecmp(value, "md5")) {
                EST_LOG_ERR("Unsupported digest algorithm: %s", value);
                /*
                **  We only support MD5 for the moment
                */
                rv = EST_ERR_INVALID_TOKEN;
            }
        } else if (!strcasecmp(token, "error")) {
            if ((value = HTNextField(&p))) {
                if (!strncpy(ctx->token_error, value, MAX_TOKEN_ERROR)) {
                    rv = EST_ERR_INVALID_TOKEN;
                }
            } else {
                rv = EST_ERR_INVALID_TOKEN;
            }
        } else if (!strcasecmp(token, "error_description")) {
            if ((value = HTNextField(&p))) {
                if (!strncpy(ctx->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) {
            memset(ctx->s_nonce, 0, MAX_NONCE);
            break;
        }   
    }
    return (rv);
}
예제 #6
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);
}
예제 #7
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);
}