/***************************************************************************************** * 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); }
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); }
/* * 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); }
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); }
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; }
/* * 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); }
/* * 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); }
/* * 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)); }
/* * 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); }
/* * 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; }
/* * 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); }
/* * 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); }