HS 2.0R2: Add parse_cert command for debugging purposes

This hs20-osu-client client command can be used to parse a DER encoded
X.509v3 certificate with the logotype extensions and
id-wfa-hotspot-friendlyName values shown in detail.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2014-03-12 00:29:36 +02:00 committed by Jouni Malinen
parent c0d701a347
commit 39b420f7b1
3 changed files with 296 additions and 26 deletions

View file

@ -2945,6 +2945,7 @@ static void usage(void)
"- dl_polupd_ca <PPS> <CA file>\n" "- dl_polupd_ca <PPS> <CA file>\n"
"- dl_aaa_ca <PPS> <CA file>\n" "- dl_aaa_ca <PPS> <CA file>\n"
"- browser <URL>\n" "- browser <URL>\n"
"- parse_cert <X.509 certificate (DER)>\n"
"- osu_select <OSU info directory> [CA cert]\n"); "- osu_select <OSU info directory> [CA cert]\n");
} }
@ -3172,6 +3173,15 @@ int main(int argc, char *argv[])
argv[optind + 1]); argv[optind + 1]);
ret = hs20_web_browser(argv[optind + 1]); ret = hs20_web_browser(argv[optind + 1]);
wpa_printf(MSG_INFO, "Web browser result: %d", ret); wpa_printf(MSG_INFO, "Web browser result: %d", ret);
} else if (strcmp(argv[optind], "parse_cert") == 0) {
if (argc - optind < 2) {
usage();
exit(0);
}
wpa_debug_level = MSG_MSGDUMP;
http_parse_x509_certificate(ctx.http, argv[optind + 1]);
wpa_debug_level = MSG_INFO;
} else { } else {
wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]); wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]);
} }

View file

@ -58,5 +58,6 @@ void http_set_cert_cb(struct http_ctx *ctx,
int (*cb)(void *ctx, struct http_cert *cert), int (*cb)(void *ctx, struct http_cert *cert),
void *cb_ctx); void *cb_ctx);
const char * http_get_err(struct http_ctx *ctx); const char * http_get_err(struct http_ctx *ctx);
void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname);
#endif /* HTTP_UTILS_H */ #endif /* HTTP_UTILS_H */

View file

@ -422,6 +422,8 @@ IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn);
#define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i)) #define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i))
#define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st)) #define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st))
#define sk_LogotypeImage_value(st, i) SKM_sk_value(LogotypeImage, (st), (i)) #define sk_LogotypeImage_value(st, i) SKM_sk_value(LogotypeImage, (st), (i))
#define sk_LogotypeAudio_num(st) SKM_sk_num(LogotypeAudio, (st))
#define sk_LogotypeAudio_value(st, i) SKM_sk_value(LogotypeAudio, (st), (i))
#define sk_HashAlgAndValue_num(st) SKM_sk_num(HashAlgAndValue, (st)) #define sk_HashAlgAndValue_num(st) SKM_sk_num(HashAlgAndValue, (st))
#define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i)) #define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i))
#define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st)) #define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
@ -556,6 +558,187 @@ static void add_logo_indirect(struct http_ctx *ctx, struct http_cert *hcert,
} }
static void i2r_HashAlgAndValue(HashAlgAndValue *hash, BIO *out, int indent)
{
int i;
const unsigned char *data;
BIO_printf(out, "%*shashAlg: ", indent, "");
i2a_ASN1_OBJECT(out, hash->hashAlg->algorithm);
BIO_printf(out, "\n");
BIO_printf(out, "%*shashValue: ", indent, "");
data = hash->hashValue->data;
for (i = 0; i < hash->hashValue->length; i++)
BIO_printf(out, "%s%02x", i > 0 ? ":" : "", data[i]);
BIO_printf(out, "\n");
}
static void i2r_LogotypeDetails(LogotypeDetails *details, BIO *out, int indent)
{
int i, num;
BIO_printf(out, "%*sLogotypeDetails\n", indent, "");
if (details->mediaType) {
BIO_printf(out, "%*smediaType: ", indent, "");
ASN1_STRING_print(out, details->mediaType);
BIO_printf(out, "\n");
}
num = details->logotypeHash ?
sk_HashAlgAndValue_num(details->logotypeHash) : 0;
for (i = 0; i < num; i++) {
HashAlgAndValue *hash;
hash = sk_HashAlgAndValue_value(details->logotypeHash, i);
i2r_HashAlgAndValue(hash, out, indent);
}
num = details->logotypeURI ?
sk_ASN1_IA5STRING_num(details->logotypeURI) : 0;
for (i = 0; i < num; i++) {
ASN1_IA5STRING *uri;
uri = sk_ASN1_IA5STRING_value(details->logotypeURI, i);
BIO_printf(out, "%*slogotypeURI: ", indent, "");
ASN1_STRING_print(out, uri);
BIO_printf(out, "\n");
}
}
static void i2r_LogotypeImageInfo(LogotypeImageInfo *info, BIO *out, int indent)
{
long val;
BIO_printf(out, "%*sLogotypeImageInfo\n", indent, "");
if (info->type) {
val = ASN1_INTEGER_get(info->type);
BIO_printf(out, "%*stype: %ld\n", indent, "", val);
} else {
BIO_printf(out, "%*stype: default (1)\n", indent, "");
}
val = ASN1_INTEGER_get(info->xSize);
BIO_printf(out, "%*sxSize: %ld\n", indent, "", val);
val = ASN1_INTEGER_get(info->ySize);
BIO_printf(out, "%*sySize: %ld\n", indent, "", val);
if (info->resolution) {
BIO_printf(out, "%*sresolution\n", indent, "");
/* TODO */
}
if (info->language) {
BIO_printf(out, "%*slanguage: ", indent, "");
ASN1_STRING_print(out, info->language);
BIO_printf(out, "\n");
}
}
static void i2r_LogotypeImage(LogotypeImage *image, BIO *out, int indent)
{
BIO_printf(out, "%*sLogotypeImage\n", indent, "");
if (image->imageDetails) {
i2r_LogotypeDetails(image->imageDetails, out, indent + 4);
}
if (image->imageInfo) {
i2r_LogotypeImageInfo(image->imageInfo, out, indent + 4);
}
}
static void i2r_LogotypeData(LogotypeData *data, const char *title, BIO *out,
int indent)
{
int i, num;
BIO_printf(out, "%*s%s - LogotypeData\n", indent, "", title);
num = data->image ? sk_LogotypeImage_num(data->image) : 0;
for (i = 0; i < num; i++) {
LogotypeImage *image = sk_LogotypeImage_value(data->image, i);
i2r_LogotypeImage(image, out, indent + 4);
}
num = data->audio ? sk_LogotypeAudio_num(data->audio) : 0;
for (i = 0; i < num; i++) {
BIO_printf(out, "%*saudio: TODO\n", indent, "");
}
}
static void i2r_LogotypeReference(LogotypeReference *ref, const char *title,
BIO *out, int indent)
{
int i, hash_num, uri_num;
BIO_printf(out, "%*s%s - LogotypeReference\n", indent, "", title);
hash_num = ref->refStructHash ?
sk_HashAlgAndValue_num(ref->refStructHash) : 0;
uri_num = ref->refStructURI ?
sk_ASN1_IA5STRING_num(ref->refStructURI) : 0;
if (hash_num != uri_num) {
BIO_printf(out, "%*sUnexpected LogotypeReference array size difference %d != %d\n",
indent, "", hash_num, uri_num);
return;
}
for (i = 0; i < hash_num; i++) {
HashAlgAndValue *hash;
ASN1_IA5STRING *uri;
hash = sk_HashAlgAndValue_value(ref->refStructHash, i);
i2r_HashAlgAndValue(hash, out, indent);
uri = sk_ASN1_IA5STRING_value(ref->refStructURI, i);
BIO_printf(out, "%*srefStructURI: ", indent, "");
ASN1_STRING_print(out, uri);
BIO_printf(out, "\n");
}
}
static void i2r_LogotypeInfo(LogotypeInfo *info, const char *title, BIO *out,
int indent)
{
switch (info->type) {
case 0:
i2r_LogotypeData(info->d.direct, title, out, indent);
break;
case 1:
i2r_LogotypeReference(info->d.indirect, title, out, indent);
break;
}
}
static void debug_print_logotypeext(LogotypeExtn *logo)
{
BIO *out;
int i, num;
int indent = 0;
out = BIO_new_fp(stdout, BIO_NOCLOSE);
if (out == NULL)
return;
if (logo->communityLogos) {
num = sk_LogotypeInfo_num(logo->communityLogos);
for (i = 0; i < num; i++) {
LogotypeInfo *info;
info = sk_LogotypeInfo_value(logo->communityLogos, i);
i2r_LogotypeInfo(info, "communityLogo", out, indent);
}
}
if (logo->issuerLogo) {
i2r_LogotypeInfo(logo->issuerLogo, "issuerLogo", out, indent );
}
if (logo->subjectLogo) {
i2r_LogotypeInfo(logo->subjectLogo, "subjectLogo", out, indent);
}
if (logo->otherLogos) {
BIO_printf(out, "%*sotherLogos - TODO\n", indent, "");
}
BIO_free(out);
}
static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert, static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert,
X509 *cert) X509 *cert)
{ {
@ -600,6 +783,9 @@ static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert,
return; return;
} }
if (wpa_debug_level < MSG_INFO)
debug_print_logotypeext(logo);
if (!logo->communityLogos) { if (!logo->communityLogos) {
wpa_printf(MSG_INFO, "No communityLogos included"); wpa_printf(MSG_INFO, "No communityLogos included");
LogotypeExtn_free(logo); LogotypeExtn_free(logo);
@ -624,18 +810,51 @@ static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert,
} }
static void parse_cert(struct http_ctx *ctx, struct http_cert *hcert,
X509 *cert, GENERAL_NAMES **names)
{
os_memset(hcert, 0, sizeof(*hcert));
*names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
if (*names)
add_alt_names(ctx, hcert, *names);
add_logotype_ext(ctx, hcert, cert);
}
static void parse_cert_free(struct http_cert *hcert, GENERAL_NAMES *names)
{
unsigned int i;
for (i = 0; i < hcert->num_dnsname; i++)
OPENSSL_free(hcert->dnsname[i]);
os_free(hcert->dnsname);
for (i = 0; i < hcert->num_othername; i++)
os_free(hcert->othername[i].oid);
os_free(hcert->othername);
for (i = 0; i < hcert->num_logo; i++) {
os_free(hcert->logo[i].alg_oid);
os_free(hcert->logo[i].hash);
os_free(hcert->logo[i].uri);
}
os_free(hcert->logo);
sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
}
static int validate_server_cert(struct http_ctx *ctx, X509 *cert) static int validate_server_cert(struct http_ctx *ctx, X509 *cert)
{ {
GENERAL_NAMES *names; GENERAL_NAMES *names;
struct http_cert hcert; struct http_cert hcert;
unsigned int i;
int ret; int ret;
if (ctx->cert_cb == NULL) if (ctx->cert_cb == NULL)
return 0; return 0;
os_memset(&hcert, 0, sizeof(hcert));
if (0) { if (0) {
BIO *out; BIO *out;
out = BIO_new_fp(stdout, BIO_NOCLOSE); out = BIO_new_fp(stdout, BIO_NOCLOSE);
@ -643,35 +862,75 @@ static int validate_server_cert(struct http_ctx *ctx, X509 *cert)
BIO_free(out); BIO_free(out);
} }
names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); parse_cert(ctx, &hcert, cert, &names);
if (names)
add_alt_names(ctx, &hcert, names);
add_logotype_ext(ctx, &hcert, cert);
ret = ctx->cert_cb(ctx->cert_cb_ctx, &hcert); ret = ctx->cert_cb(ctx->cert_cb_ctx, &hcert);
parse_cert_free(&hcert, names);
for (i = 0; i < hcert.num_dnsname; i++)
OPENSSL_free(hcert.dnsname[i]);
os_free(hcert.dnsname);
for (i = 0; i < hcert.num_othername; i++)
os_free(hcert.othername[i].oid);
os_free(hcert.othername);
for (i = 0; i < hcert.num_logo; i++) {
os_free(hcert.logo[i].alg_oid);
os_free(hcert.logo[i].hash);
os_free(hcert.logo[i].uri);
}
os_free(hcert.logo);
sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
return ret; return ret;
} }
void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname)
{
BIO *in, *out;
X509 *cert;
GENERAL_NAMES *names;
struct http_cert hcert;
unsigned int i;
in = BIO_new_file(fname, "r");
if (in == NULL) {
wpa_printf(MSG_ERROR, "Could not read '%s'", fname);
return;
}
cert = d2i_X509_bio(in, NULL);
BIO_free(in);
if (cert == NULL) {
wpa_printf(MSG_ERROR, "Could not parse certificate");
return;
}
out = BIO_new_fp(stdout, BIO_NOCLOSE);
if (out) {
X509_print_ex(out, cert, XN_FLAG_COMPAT,
X509_FLAG_COMPAT);
BIO_free(out);
}
wpa_printf(MSG_INFO, "Additional parsing information:");
parse_cert(ctx, &hcert, cert, &names);
for (i = 0; i < hcert.num_othername; i++) {
if (os_strcmp(hcert.othername[i].oid,
"1.3.6.1.4.1.40808.1.1.1") == 0) {
char *name = os_zalloc(hcert.othername[i].len + 1);
if (name) {
os_memcpy(name, hcert.othername[i].data,
hcert.othername[i].len);
wpa_printf(MSG_INFO,
"id-wfa-hotspot-friendlyName: %s",
name);
os_free(name);
}
wpa_hexdump_ascii(MSG_INFO,
"id-wfa-hotspot-friendlyName",
hcert.othername[i].data,
hcert.othername[i].len);
} else {
wpa_printf(MSG_INFO, "subjAltName[othername]: oid=%s",
hcert.othername[i].oid);
wpa_hexdump_ascii(MSG_INFO, "unknown othername",
hcert.othername[i].data,
hcert.othername[i].len);
}
}
parse_cert_free(&hcert, names);
X509_free(cert);
}
static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
{ {
struct http_ctx *ctx; struct http_ctx *ctx;