/* * This routine intializes an EST context, which can later * be used to issue commands to an EST server. */ static EST_CTX * setup_est_context (void) { EST_CTX *ectx; EST_ERROR rv; /* * Initialize an EST context. We must provide the trust * anchor certs at this time. */ ectx = est_client_init(cacerts, cacerts_len, EST_CERT_FORMAT_PEM, NULL); if (!ectx) { printf("\nUnable to initialize EST context. Aborting!!!\n"); exit(1); } /* * Set the local authentication credentials. We're not using * a certificate to identify ourselves to the server. We're * simply hard-coding the userID and password, which will be * used for HTTP authentication. */ rv = est_client_set_auth(ectx, est_http_uid, est_http_pwd, NULL, NULL); if (rv != EST_ERR_NONE) { printf("\nUnable to configure client authentication. Aborting!!!\n"); printf("EST error code %d (%s)\n", rv, EST_ERR_NUM_TO_STR(rv)); exit(1); } if (srp) { rv = est_client_enable_srp(ectx, SRP_MINIMAL_N, est_srp_uid, est_srp_pwd); if (rv != EST_ERR_NONE) { printf("\nUnable to enable SRP. Aborting!!!\n"); exit(1); } } if (token_auth_mode) { rv = est_client_set_auth_cred_cb(ectx, auth_credentials_token_cb); if (rv != EST_ERR_NONE) { printf("\nUnable to register token auth callback. Aborting!!!\n"); exit(1); } } /* * Specify the EST server address and TCP port# */ rv = est_client_set_server(ectx, est_server, est_port, NULL); if (rv != EST_ERR_NONE) { printf("\nUnable to configure server address. Aborting!!!\n"); printf("EST error code %d (%s)\n", rv, EST_ERR_NUM_TO_STR(rv)); exit(1); } return (ectx); }
static void do_operation () { EST_CTX *ectx; unsigned char *pkcs7; int pkcs7_len = 0; int rv; char file_name[MAX_FILENAME_LEN]; unsigned char *new_client_cert; int retry_delay = 0; time_t retry_time = 0; char *operation; ectx = est_client_init(cacerts, cacerts_len, EST_CERT_FORMAT_PEM, client_manual_cert_verify); if (!ectx) { printf("\nUnable to initialize EST context. Aborting!!!\n"); exit(1); } rv = est_client_set_read_timeout(ectx, read_timeout); if (rv != EST_ERR_NONE) { printf("\nUnable to configure read timeout from server. Aborting!!!\n"); printf("EST error code %d (%s)\n", rv, EST_ERR_NUM_TO_STR(rv)); exit(1); } rv = est_client_set_auth(ectx, est_http_uid, est_http_pwd, client_cert, client_priv_key); if (rv != EST_ERR_NONE) { printf("\nUnable to configure client authentication. Aborting!!!\n"); printf("EST error code %d (%s)\n", rv, EST_ERR_NUM_TO_STR(rv)); exit(1); } if (srp) { rv = est_client_enable_srp(ectx, 1024, est_srp_uid, est_srp_pwd); if (rv != EST_ERR_NONE) { printf("\nUnable to enable SRP. Aborting!!!\n"); exit(1); } } if (token_auth_mode) { rv = est_client_set_auth_cred_cb(ectx, auth_credentials_token_cb); if (rv != EST_ERR_NONE) { printf("\nUnable to register token auth callback. Aborting!!!\n"); exit(1); } } est_client_set_server(ectx, est_server, est_port); if (getcert) { operation = "Get CA Cert"; rv = est_client_get_cacerts(ectx, &pkcs7_len); if (rv == EST_ERR_NONE) { if (verbose) { printf("\nGet CA Cert success\n"); } /* * allocate a buffer to retrieve the CA certs * and get them copied in */ pkcs7 = malloc(pkcs7_len); rv = est_client_copy_cacerts(ectx, pkcs7); /* * Dump the retrieved cert to stdout */ if (verbose) { dumpbin(pkcs7, pkcs7_len); } /* * Generate the output file name, which contains the thread ID * and iteration number. */ snprintf(file_name, MAX_FILENAME_LEN, "%s/cacert.pkcs7", out_dir); write_binary_file(file_name, pkcs7, pkcs7_len); free(pkcs7); } } if (enroll && getcsr) { operation = "Regular enrollment with server-defined attributes"; rv = regular_enroll_attempt(ectx); if (rv == EST_ERR_CA_ENROLL_RETRY) { /* * go get the retry period */ rv = est_client_copy_retry_after(ectx, &retry_delay, &retry_time); if (verbose) { printf("\nretry after period copy rv = %d " "Retry-After delay seconds = %d " "Retry-After delay time = %s\n", rv, retry_delay, ctime(&retry_time) ); } if (rv == EST_ERR_NONE) { retry_enroll_delay(retry_delay, retry_time); } /* * now that we're back, try to enroll again */ rv = regular_enroll_attempt(ectx); } } else if (enroll && !getcsr) { operation = "Simple enrollment without server-defined attributes"; rv = simple_enroll_attempt(ectx); if (rv == EST_ERR_CA_ENROLL_RETRY) { /* * go get the retry period */ rv = est_client_copy_retry_after(ectx, &retry_delay, &retry_time); if (verbose) { printf("\nretry after period copy rv = %d " "Retry-After delay seconds = %d " "Retry-After delay time = %s\n", rv, retry_delay, ctime(&retry_time) ); } if (rv == EST_ERR_NONE) { retry_enroll_delay(retry_delay, retry_time); } /* * now that we're back, try to enroll again */ rv = simple_enroll_attempt(ectx); } } else if (!enroll && getcsr) { operation = "Get CSR attribues"; rv = regular_csr_attempt(ectx); } /* Split reenroll from enroll to allow both messages to be sent */ if (reenroll) { operation = "Re-enrollment"; rv = est_client_reenroll(ectx, client_cert, &pkcs7_len, client_priv_key); if (verbose) { printf("\nreenroll rv = %d (%s) with pkcs7 length = %d\n", rv, EST_ERR_NUM_TO_STR(rv), pkcs7_len); } if (rv == EST_ERR_NONE) { /* * client library has obtained the new client certificate. * now retrieve it from the library */ new_client_cert = malloc(pkcs7_len); if (new_client_cert == NULL) { if (verbose) { printf("\nmalloc of destination buffer for reenroll cert failed\n"); } } rv = est_client_copy_enrolled_cert(ectx, new_client_cert); if (verbose) { printf("\nreenroll copy rv = %d\n", rv); } if (rv == EST_ERR_NONE) { /* * Enrollment copy worked, dump the pkcs7 cert to stdout */ if (verbose) { dumpbin(new_client_cert, pkcs7_len); } } /* * Generate the output file name, which contains the thread ID * and iteration number. */ snprintf(file_name, MAX_FILENAME_LEN, "%s/newcert", out_dir); save_cert(file_name, new_client_cert, pkcs7_len); free(new_client_cert); } } if (rv != EST_ERR_NONE) { /* * something went wrong. */ printf("\n%s failed with code %d (%s)\n", operation, rv, EST_ERR_NUM_TO_STR(rv)); } est_destroy(ectx); ERR_clear_error(); ERR_remove_thread_state(NULL); }
static int regular_enroll_attempt (EST_CTX *ectx) { int pkcs7_len = 0; int rv; char file_name[MAX_FILENAME_LEN]; unsigned char *new_client_cert; unsigned char *attr_data = NULL; unsigned char *der_ptr = NULL; int attr_len, der_len, nid; X509_REQ *csr; /* * We need to get the CSR attributes first, which allows libest * to know if the challengePassword needs to be included in the * CSR. */ rv = est_client_get_csrattrs(ectx, &attr_data, &attr_len); if (rv != EST_ERR_NONE) { printf("\nWarning: CSR attributes were not available"); return (rv); } /* Generate a CSR */ csr = X509_REQ_new(); if (csr == NULL) { printf("\nFailed to get X509_REQ"); return (EST_ERR_NO_CSR); } rv = populate_x509_csr(csr, priv_key, "EST-client"); if (rv) { printf("\nFailed to populate X509_REQ"); return (EST_ERR_X509_PUBKEY); } rv = est_decode_attributes_helper((char*)attr_data, attr_len, &der_ptr, &der_len); if (rv != EST_ERR_NONE) { printf("\nFailed to decode attributes"); return (rv); } while (der_len) { rv = est_get_attributes_helper(&der_ptr, &der_len, &nid); if (rv == EST_ERR_NONE) { /* * This switch can be enhanced to include all NID values * of interest by the client/server. In addition the last * parameter can be enhanced to provide the character string * type information that is included with the NID. * * Presently only character string types are supported, but at * some point OID or groups of strings/OIDs may need to be * supported. * * Note that challenge password should not be included here * as it is handled by libest client code. */ switch (nid) { case NID_commonName: /* add the attribute to the request */ rv = est_add_attributes_helper(csr, nid, "test\n", 0); break; case NID_pkcs9_emailAddress: /* add the attribute to the request */ rv = est_add_attributes_helper(csr, nid, "[email protected]\0", 0); break; case NID_undef: printf("\nNID is undefined; skipping it\n"); break; default: rv = est_add_attributes_helper(csr, nid, "", 0); break; } if (rv != EST_ERR_NONE) { printf("\n Error adding NID=%d", nid); } } } X509_REQ_print_fp(stderr, csr); rv = est_client_enroll_csr(ectx, csr, &pkcs7_len, priv_key); if (verbose) { printf("\nenrollment rv = %d (%s) with pkcs7 length = %d\n", rv, EST_ERR_NUM_TO_STR(rv), pkcs7_len); } if (rv == EST_ERR_NONE) { /* * client library has obtained the new client certificate. * now retrieve it from the library */ new_client_cert = malloc(pkcs7_len); if (new_client_cert == NULL) { if (verbose) { printf("\nmalloc of destination buffer for enrollment cert failed\n"); } return (EST_ERR_MALLOC); } rv = est_client_copy_enrolled_cert(ectx, new_client_cert); if (verbose) { printf("\nenrollment copy rv = %d\n", rv); } if (rv == EST_ERR_NONE) { /* * Enrollment copy worked, dump the pkcs7 cert to stdout */ if (verbose) { dumpbin(new_client_cert, pkcs7_len); } } snprintf(file_name, MAX_FILENAME_LEN, "%s/newcert", out_dir); save_cert(file_name, new_client_cert, pkcs7_len); free(new_client_cert); } return (rv); }
static int simple_enroll_attempt (EST_CTX *ectx) { int pkcs7_len = 0; int rv; char file_name[MAX_FILENAME_LEN]; unsigned char *new_client_cert; X509_REQ *csr = NULL; if (force_pop) { rv = est_client_force_pop(ectx); if (rv != EST_ERR_NONE) { printf("\nFailed to enable force PoP"); } } if (csr_file[0]) { csr = read_csr(csr_file); if (csr == NULL) { rv = EST_ERR_PEM_READ; }else { rv = est_client_enroll_csr(ectx, csr, &pkcs7_len, NULL); } }else { rv = est_client_enroll(ectx, subj_cn, &pkcs7_len, priv_key); } if (csr) { X509_REQ_free(csr); } if (verbose) { printf("\nenrollment rv = %d (%s) with pkcs7 length = %d\n", rv, EST_ERR_NUM_TO_STR(rv), pkcs7_len); } if (rv == EST_ERR_NONE) { /* * client library has obtained the new client certificate. * now retrieve it from the library */ new_client_cert = malloc(pkcs7_len); if (new_client_cert == NULL) { if (verbose) { printf("\nmalloc of destination buffer for enrollment cert failed\n"); } return (EST_ERR_MALLOC); } rv = est_client_copy_enrolled_cert(ectx, new_client_cert); if (verbose) { printf("\nenrollment copy rv = %d\n", rv); } if (rv == EST_ERR_NONE) { /* * Enrollment copy worked, dump the pkcs7 cert to stdout */ if (verbose) { dumpbin(new_client_cert, pkcs7_len); } } snprintf(file_name, MAX_FILENAME_LEN, "%s/newcert", out_dir); save_cert(file_name, new_client_cert, pkcs7_len); free(new_client_cert); } return (rv); }
/* * 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); }
/* * est_proxy_retrieve_cacerts() issues a request to the server to obtain the * CA Certs chain to be used for Get CA Certs requests from clients. * The CA Cert chain returned from the server are passed back to the caller. * * It's the responsibility of the caller to free up this buffer. */ EST_ERROR est_proxy_retrieve_cacerts (EST_CTX *ctx, unsigned char **cacerts_rtn, int *cacerts_rtn_len) { EST_CTX *client_ctx; EST_ERROR rv; int rcvd_cacerts_len; unsigned char *rcvd_cacerts; if (ctx == NULL) { EST_LOG_ERR("Ctx not passed to %s", __FUNCTION__); return (EST_ERR_NO_CTX); } if (cacerts_rtn == NULL || cacerts_rtn_len == NULL) { EST_LOG_ERR("Ctx not passed to %s", __FUNCTION__); return (EST_ERR_INVALID_PARAMETERS); } *cacerts_rtn = NULL; *cacerts_rtn_len = 0; /* * 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); } rv = est_client_get_cacerts(client_ctx, &rcvd_cacerts_len); if (rv != EST_ERR_NONE) { EST_LOG_ERR("Unable to retrieve CA Certs from upstream server RC = %s", EST_ERR_NUM_TO_STR(rv)); return (rv); } /* * Allocate a buffer to retrieve the CA certs * and get them copied in */ rcvd_cacerts = malloc(rcvd_cacerts_len); if (rcvd_cacerts == NULL) { EST_LOG_ERR("Unable to malloc buffer for cacerts received from server"); return (EST_ERR_MALLOC); } rv = est_client_copy_cacerts(client_ctx, rcvd_cacerts); if (rv != EST_ERR_NONE) { EST_LOG_ERR("Unable to copy CA Certs from upstream server RC = %s", EST_ERR_NUM_TO_STR(rv)); free(rcvd_cacerts); return (rv); } /* * The retrieving of the CA certs through the normal client * interface causes the client to go back into an uninitialized state. * In this case though, we're getting it just for passing it back * to the downstream clients, so we're going to put this client * context back into the initialized state */ client_ctx->est_client_initialized = 1; *cacerts_rtn = rcvd_cacerts; *cacerts_rtn_len = rcvd_cacerts_len; return (EST_ERR_NONE); }
int main (int argc, char **argv) { EST_ERROR rv; char c; char *key_data; EVP_PKEY *key; char *trustanchor_file; EST_CTX *ectx; int p7_len; int ca_certs_len; unsigned char *new_client_cert; unsigned char *new_certs; static struct option long_options[] = { {"srp", 0, 0, 0}, {"srp-user", 1, 0, 0}, {"srp-password", 1, 0, 0}, {"auth-token", 1, 0, 0}, {NULL, 0, NULL, 0} }; int option_index = 0; est_http_uid[0] = 0x0; est_http_pwd[0] = 0x0; while ((c = getopt_long(argc, argv, "s:p:u:h:", long_options, &option_index)) != -1) { switch (c) { case 0: if (!strncmp(long_options[option_index].name,"srp", strlen("srp"))) { srp = 1; } if (!strncmp(long_options[option_index].name,"srp-user", strlen("srp-user"))) { strncpy(est_srp_uid, optarg, MAX_UID_LEN); } if (!strncmp(long_options[option_index].name,"srp-password", strlen("srp-password"))) { strncpy(est_srp_pwd, optarg, MAX_PWD_LEN); } if (!strncmp(long_options[option_index].name,"auth-token", strlen("auth-token"))) { strncpy(est_auth_token, optarg, MAX_AUTH_TOKEN_LEN); token_auth_mode = 1; } break; case 'u': strncpy(est_http_uid, optarg, MAX_UID_LEN); break; case 'h': strncpy(est_http_pwd, optarg, MAX_PWD_LEN); break; case 's': strncpy(est_server, optarg, MAX_SERVER_LEN); break; case 'p': est_port = atoi(optarg); break; default: show_usage_and_exit(); break; } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } argc -= optind; argv += optind; if (est_http_uid[0] && !est_http_pwd[0]) { printf ("Error: The password for HTTP authentication must be specified when the HTTP user name is set.\n"); exit(1); } /* * Initialize the library, including OpenSSL */ est_apps_startup(); print_version(); printf("\nUsing EST server %s:%d", est_server, est_port); /* * Read in the trusted certificates, which are used by * libEST to verify the identity of the EST server. */ trustanchor_file = getenv("EST_OPENSSL_CACERT"); cacerts_len = read_binary_file(trustanchor_file, &cacerts); if (cacerts_len <= 0) { printf("\nTrusted certs file could not be read. Did you set EST_OPENSSL_CACERT?\n"); exit(1); } /* * This is not required, but we'll enable full debugs */ #ifndef WIN32 /* Initialize the EST logging */ est_init_logger(EST_LOG_LVL_INFO, NULL); #else InitializeCriticalSection (&logger_critical_section); est_init_logger(EST_LOG_LVL_INFO, &windows_logger_stderr); #endif /* * Create a public/private key pair that will be used for * the enrollment. We'll write this out to a local * file called new_key.pem. */ key_data = generate_private_RSA_key(2048, NULL/* no password_cb */); write_binary_file("./new_key.pem", (unsigned char *)key_data, strlen(key_data)); /* * Use the load_clear macro to load in an unencrypted key */ key = load_clear_private_key_PEM(key_data); if(!key) { printf("\nUnable to load newly created key from PEM file\n"); exit(1); } memset(key_data, 0, strlen(key_data)); free(key_data); key_data = NULL; ectx = setup_est_context(); if (!ectx) { printf("\nUnable to create EST client context\n"); exit(1); } /* * Attempt to provision a new cert */ rv = est_client_provision_cert(ectx, "localhost", &p7_len, &ca_certs_len, key); if (rv != EST_ERR_NONE) { printf("\nProvisioning failed with error %s\n", EST_ERR_NUM_TO_STR(rv)); exit(1); } EVP_PKEY_free(key); /* * Retrieve a copy of the cert */ new_client_cert = malloc(p7_len); if (new_client_cert == NULL){ printf("\nFailed to allocate memory for the newly provisioned cert\n"); exit(1); } rv = est_client_copy_enrolled_cert(ectx, new_client_cert); if (rv != EST_ERR_NONE) { printf("\nFailed to copy new cert with code %d (%s)\n", rv, EST_ERR_NUM_TO_STR(rv)); exit(1); } /* * Save the cert to local storage */ write_binary_file(cert_file_name, new_client_cert, p7_len); free(new_client_cert); /* * Retrieve a copy of the new trust anchor */ new_certs = malloc(ca_certs_len); rv = est_client_copy_cacerts(ectx, new_certs); if (rv != EST_ERR_NONE) { printf("\nFailed to copy new CA certs with code %d (%s)\n", rv, EST_ERR_NUM_TO_STR(rv)); exit(1); } /* * Your appliations should save the CA certs to local storage in case * they're needed for future use. */ write_binary_file(ca_file_name, new_certs, ca_certs_len); free(new_certs); printf("\n\nSuccess!!!\n"); free(cacerts); est_destroy(ectx); est_apps_shutdown(); printf("\n"); return 0; }