Rich Salz | 440e5d8 | 2016-05-17 14:20:24 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved. |
| 3 | * |
| 4 | * Licensed under the OpenSSL license (the "License"). You may not use |
| 5 | * this file except in compliance with the License. You can obtain a copy |
| 6 | * in the file LICENSE in the source distribution or at |
| 7 | * https://www.openssl.org/source/license.html |
| 8 | */ |
| 9 | |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 10 | #include <openssl/x509.h> |
| 11 | #include <openssl/x509v3.h> |
Dr. Stephen Henson | bdcf772 | 2012-12-26 23:51:56 +0000 | [diff] [blame] | 12 | #include "../e_os.h" |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 13 | #include <string.h> |
| 14 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 15 | static const char *const names[] = { |
| 16 | "a", "b", ".", "*", "@", |
| 17 | ".a", "a.", ".b", "b.", ".*", "*.", "*@", "@*", "a@", "@a", "b@", "..", |
Zi Lin | 9f9a392 | 2016-01-15 14:31:11 -0500 | [diff] [blame] | 18 | "-example.com", "example-.com", |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 19 | "@@", "**", "*.com", "*com", "*.*.com", "*com", "com*", "*example.com", |
| 20 | "*@example.com", "test@*.example.com", "example.com", "www.example.com", |
| 21 | "test.www.example.com", "*.example.com", "*.www.example.com", |
| 22 | "test.*.example.com", "www.*.com", |
| 23 | ".www.example.com", "*www.example.com", |
| 24 | "example.net", "xn--rger-koa.example.com", |
Zi Lin | 9f9a392 | 2016-01-15 14:31:11 -0500 | [diff] [blame] | 25 | "*.xn--rger-koa.example.com", "www.xn--rger-koa.example.com", |
| 26 | "*.good--example.com", "www.good--example.com", |
| 27 | "*.xn--bar.com", "xn--foo.xn--bar.com", |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 28 | "a.example.com", "b.example.com", |
| 29 | "postmaster@example.com", "Postmaster@example.com", |
| 30 | "postmaster@EXAMPLE.COM", |
| 31 | NULL |
| 32 | }; |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 33 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 34 | static const char *const exceptions[] = { |
| 35 | "set CN: host: [*.example.com] matches [a.example.com]", |
| 36 | "set CN: host: [*.example.com] matches [b.example.com]", |
| 37 | "set CN: host: [*.example.com] matches [www.example.com]", |
| 38 | "set CN: host: [*.example.com] matches [xn--rger-koa.example.com]", |
| 39 | "set CN: host: [*.www.example.com] matches [test.www.example.com]", |
| 40 | "set CN: host: [*.www.example.com] matches [.www.example.com]", |
| 41 | "set CN: host: [*www.example.com] matches [www.example.com]", |
| 42 | "set CN: host: [test.www.example.com] matches [.www.example.com]", |
Zi Lin | 9f9a392 | 2016-01-15 14:31:11 -0500 | [diff] [blame] | 43 | "set CN: host: [*.xn--rger-koa.example.com] matches [www.xn--rger-koa.example.com]", |
| 44 | "set CN: host: [*.xn--bar.com] matches [xn--foo.xn--bar.com]", |
| 45 | "set CN: host: [*.good--example.com] matches [www.good--example.com]", |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 46 | "set CN: host-no-wildcards: [*.www.example.com] matches [.www.example.com]", |
| 47 | "set CN: host-no-wildcards: [test.www.example.com] matches [.www.example.com]", |
| 48 | "set emailAddress: email: [postmaster@example.com] does not match [Postmaster@example.com]", |
| 49 | "set emailAddress: email: [postmaster@EXAMPLE.COM] does not match [Postmaster@example.com]", |
| 50 | "set emailAddress: email: [Postmaster@example.com] does not match [postmaster@example.com]", |
| 51 | "set emailAddress: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]", |
| 52 | "set dnsName: host: [*.example.com] matches [www.example.com]", |
| 53 | "set dnsName: host: [*.example.com] matches [a.example.com]", |
| 54 | "set dnsName: host: [*.example.com] matches [b.example.com]", |
| 55 | "set dnsName: host: [*.example.com] matches [xn--rger-koa.example.com]", |
| 56 | "set dnsName: host: [*.www.example.com] matches [test.www.example.com]", |
| 57 | "set dnsName: host-no-wildcards: [*.www.example.com] matches [.www.example.com]", |
| 58 | "set dnsName: host-no-wildcards: [test.www.example.com] matches [.www.example.com]", |
| 59 | "set dnsName: host: [*.www.example.com] matches [.www.example.com]", |
| 60 | "set dnsName: host: [*www.example.com] matches [www.example.com]", |
| 61 | "set dnsName: host: [test.www.example.com] matches [.www.example.com]", |
Zi Lin | 9f9a392 | 2016-01-15 14:31:11 -0500 | [diff] [blame] | 62 | "set dnsName: host: [*.xn--rger-koa.example.com] matches [www.xn--rger-koa.example.com]", |
| 63 | "set dnsName: host: [*.xn--bar.com] matches [xn--foo.xn--bar.com]", |
| 64 | "set dnsName: host: [*.good--example.com] matches [www.good--example.com]", |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 65 | "set rfc822Name: email: [postmaster@example.com] does not match [Postmaster@example.com]", |
| 66 | "set rfc822Name: email: [Postmaster@example.com] does not match [postmaster@example.com]", |
| 67 | "set rfc822Name: email: [Postmaster@example.com] does not match [postmaster@EXAMPLE.COM]", |
| 68 | "set rfc822Name: email: [postmaster@EXAMPLE.COM] does not match [Postmaster@example.com]", |
| 69 | NULL |
| 70 | }; |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 71 | |
| 72 | static int is_exception(const char *msg) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 73 | { |
| 74 | const char *const *p; |
| 75 | for (p = exceptions; *p; ++p) |
| 76 | if (strcmp(msg, *p) == 0) |
| 77 | return 1; |
| 78 | return 0; |
| 79 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 80 | |
| 81 | static int set_cn(X509 *crt, ...) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 82 | { |
| 83 | int ret = 0; |
| 84 | X509_NAME *n = NULL; |
| 85 | va_list ap; |
| 86 | va_start(ap, crt); |
| 87 | n = X509_NAME_new(); |
| 88 | if (n == NULL) |
| 89 | goto out; |
| 90 | while (1) { |
| 91 | int nid; |
| 92 | const char *name; |
| 93 | nid = va_arg(ap, int); |
| 94 | if (nid == 0) |
| 95 | break; |
| 96 | name = va_arg(ap, const char *); |
| 97 | if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_ASC, |
| 98 | (unsigned char *)name, -1, -1, 1)) |
| 99 | goto out; |
| 100 | } |
| 101 | if (!X509_set_subject_name(crt, n)) |
| 102 | goto out; |
| 103 | ret = 1; |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 104 | out: |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 105 | X509_NAME_free(n); |
| 106 | va_end(ap); |
| 107 | return ret; |
| 108 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 109 | |
Tim Hudson | 1d97c84 | 2014-12-28 12:48:40 +1000 | [diff] [blame] | 110 | /*- |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 111 | int X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc); |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 112 | X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex, |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 113 | int nid, int crit, ASN1_OCTET_STRING *data); |
| 114 | int X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc); |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 115 | */ |
| 116 | |
| 117 | static int set_altname(X509 *crt, ...) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 118 | { |
| 119 | int ret = 0; |
| 120 | GENERAL_NAMES *gens = NULL; |
| 121 | GENERAL_NAME *gen = NULL; |
| 122 | ASN1_IA5STRING *ia5 = NULL; |
| 123 | va_list ap; |
| 124 | va_start(ap, crt); |
| 125 | gens = sk_GENERAL_NAME_new_null(); |
| 126 | if (gens == NULL) |
| 127 | goto out; |
| 128 | while (1) { |
| 129 | int type; |
| 130 | const char *name; |
| 131 | type = va_arg(ap, int); |
| 132 | if (type == 0) |
| 133 | break; |
| 134 | name = va_arg(ap, const char *); |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 135 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 136 | gen = GENERAL_NAME_new(); |
| 137 | if (gen == NULL) |
| 138 | goto out; |
| 139 | ia5 = ASN1_IA5STRING_new(); |
| 140 | if (ia5 == NULL) |
| 141 | goto out; |
| 142 | if (!ASN1_STRING_set(ia5, name, -1)) |
| 143 | goto out; |
| 144 | switch (type) { |
| 145 | case GEN_EMAIL: |
| 146 | case GEN_DNS: |
| 147 | GENERAL_NAME_set0_value(gen, type, ia5); |
| 148 | ia5 = NULL; |
| 149 | break; |
| 150 | default: |
| 151 | abort(); |
| 152 | } |
| 153 | sk_GENERAL_NAME_push(gens, gen); |
| 154 | gen = NULL; |
| 155 | } |
| 156 | if (!X509_add1_ext_i2d(crt, NID_subject_alt_name, gens, 0, 0)) |
| 157 | goto out; |
| 158 | ret = 1; |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 159 | out: |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 160 | ASN1_IA5STRING_free(ia5); |
| 161 | GENERAL_NAME_free(gen); |
| 162 | GENERAL_NAMES_free(gens); |
| 163 | va_end(ap); |
| 164 | return ret; |
| 165 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 166 | |
| 167 | static int set_cn1(X509 *crt, const char *name) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 168 | { |
| 169 | return set_cn(crt, NID_commonName, name, 0); |
| 170 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 171 | |
| 172 | static int set_cn_and_email(X509 *crt, const char *name) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 173 | { |
| 174 | return set_cn(crt, NID_commonName, name, |
| 175 | NID_pkcs9_emailAddress, "dummy@example.com", 0); |
| 176 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 177 | |
| 178 | static int set_cn2(X509 *crt, const char *name) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 179 | { |
| 180 | return set_cn(crt, NID_commonName, "dummy value", |
| 181 | NID_commonName, name, 0); |
| 182 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 183 | |
| 184 | static int set_cn3(X509 *crt, const char *name) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 185 | { |
| 186 | return set_cn(crt, NID_commonName, name, |
| 187 | NID_commonName, "dummy value", 0); |
| 188 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 189 | |
| 190 | static int set_email1(X509 *crt, const char *name) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 191 | { |
| 192 | return set_cn(crt, NID_pkcs9_emailAddress, name, 0); |
| 193 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 194 | |
| 195 | static int set_email2(X509 *crt, const char *name) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 196 | { |
| 197 | return set_cn(crt, NID_pkcs9_emailAddress, "dummy@example.com", |
| 198 | NID_pkcs9_emailAddress, name, 0); |
| 199 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 200 | |
| 201 | static int set_email3(X509 *crt, const char *name) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 202 | { |
| 203 | return set_cn(crt, NID_pkcs9_emailAddress, name, |
| 204 | NID_pkcs9_emailAddress, "dummy@example.com", 0); |
| 205 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 206 | |
| 207 | static int set_email_and_cn(X509 *crt, const char *name) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 208 | { |
| 209 | return set_cn(crt, NID_pkcs9_emailAddress, name, |
| 210 | NID_commonName, "www.example.org", 0); |
| 211 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 212 | |
| 213 | static int set_altname_dns(X509 *crt, const char *name) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 214 | { |
| 215 | return set_altname(crt, GEN_DNS, name, 0); |
| 216 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 217 | |
| 218 | static int set_altname_email(X509 *crt, const char *name) |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 219 | { |
| 220 | return set_altname(crt, GEN_EMAIL, name, 0); |
| 221 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 222 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 223 | struct set_name_fn { |
| 224 | int (*fn) (X509 *, const char *); |
| 225 | const char *name; |
| 226 | int host; |
| 227 | int email; |
| 228 | }; |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 229 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 230 | static const struct set_name_fn name_fns[] = { |
| 231 | {set_cn1, "set CN", 1, 0}, |
| 232 | {set_cn2, "set CN", 1, 0}, |
| 233 | {set_cn3, "set CN", 1, 0}, |
| 234 | {set_cn_and_email, "set CN", 1, 0}, |
| 235 | {set_email1, "set emailAddress", 0, 1}, |
| 236 | {set_email2, "set emailAddress", 0, 1}, |
| 237 | {set_email3, "set emailAddress", 0, 1}, |
| 238 | {set_email_and_cn, "set emailAddress", 0, 1}, |
| 239 | {set_altname_dns, "set dnsName", 1, 0}, |
| 240 | {set_altname_email, "set rfc822Name", 0, 1}, |
| 241 | {NULL, NULL, 0} |
| 242 | }; |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 243 | |
| 244 | static X509 *make_cert() |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 245 | { |
| 246 | X509 *ret = NULL; |
| 247 | X509 *crt = NULL; |
| 248 | X509_NAME *issuer = NULL; |
| 249 | crt = X509_new(); |
| 250 | if (crt == NULL) |
| 251 | goto out; |
| 252 | if (!X509_set_version(crt, 3)) |
| 253 | goto out; |
| 254 | ret = crt; |
| 255 | crt = NULL; |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 256 | out: |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 257 | X509_NAME_free(issuer); |
| 258 | return ret; |
| 259 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 260 | |
| 261 | static int errors; |
| 262 | |
| 263 | static void check_message(const struct set_name_fn *fn, const char *op, |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 264 | const char *nameincert, int match, const char *name) |
| 265 | { |
| 266 | char msg[1024]; |
| 267 | if (match < 0) |
| 268 | return; |
| 269 | BIO_snprintf(msg, sizeof(msg), "%s: %s: [%s] %s [%s]", |
| 270 | fn->name, op, nameincert, |
| 271 | match ? "matches" : "does not match", name); |
| 272 | if (is_exception(msg)) |
| 273 | return; |
| 274 | puts(msg); |
| 275 | ++errors; |
| 276 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 277 | |
| 278 | static void run_cert(X509 *crt, const char *nameincert, |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 279 | const struct set_name_fn *fn) |
| 280 | { |
| 281 | const char *const *pname = names; |
| 282 | while (*pname) { |
| 283 | int samename = strcasecmp(nameincert, *pname) == 0; |
| 284 | size_t namelen = strlen(*pname); |
| 285 | char *name = malloc(namelen); |
| 286 | int match, ret; |
| 287 | memcpy(name, *pname, namelen); |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 288 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 289 | ret = X509_check_host(crt, name, namelen, 0, NULL); |
| 290 | match = -1; |
| 291 | if (ret < 0) { |
| 292 | fprintf(stderr, "internal error in X509_check_host"); |
| 293 | ++errors; |
| 294 | } else if (fn->host) { |
| 295 | if (ret == 1 && !samename) |
| 296 | match = 1; |
| 297 | if (ret == 0 && samename) |
| 298 | match = 0; |
| 299 | } else if (ret == 1) |
| 300 | match = 1; |
| 301 | check_message(fn, "host", nameincert, match, *pname); |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 302 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 303 | ret = X509_check_host(crt, name, namelen, |
| 304 | X509_CHECK_FLAG_NO_WILDCARDS, NULL); |
| 305 | match = -1; |
| 306 | if (ret < 0) { |
| 307 | fprintf(stderr, "internal error in X509_check_host"); |
| 308 | ++errors; |
| 309 | } else if (fn->host) { |
| 310 | if (ret == 1 && !samename) |
| 311 | match = 1; |
| 312 | if (ret == 0 && samename) |
| 313 | match = 0; |
| 314 | } else if (ret == 1) |
| 315 | match = 1; |
| 316 | check_message(fn, "host-no-wildcards", nameincert, match, *pname); |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 317 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 318 | ret = X509_check_email(crt, name, namelen, 0); |
| 319 | match = -1; |
| 320 | if (fn->email) { |
| 321 | if (ret && !samename) |
| 322 | match = 1; |
| 323 | if (!ret && samename && strchr(nameincert, '@') != NULL) |
| 324 | match = 0; |
| 325 | } else if (ret) |
| 326 | match = 1; |
| 327 | check_message(fn, "email", nameincert, match, *pname); |
| 328 | ++pname; |
| 329 | free(name); |
| 330 | } |
| 331 | } |
Dr. Stephen Henson | d88926f | 2012-11-18 15:13:55 +0000 | [diff] [blame] | 332 | |
Matt Caswell | 0f113f3 | 2015-01-22 03:40:55 +0000 | [diff] [blame] | 333 | int main(void) |
| 334 | { |
| 335 | const struct set_name_fn *pfn = name_fns; |
| 336 | while (pfn->name) { |
| 337 | const char *const *pname = names; |
| 338 | while (*pname) { |
| 339 | X509 *crt = make_cert(); |
| 340 | if (crt == NULL) { |
| 341 | fprintf(stderr, "make_cert failed\n"); |
| 342 | return 1; |
| 343 | } |
| 344 | if (!pfn->fn(crt, *pname)) { |
| 345 | fprintf(stderr, "X509 name setting failed\n"); |
| 346 | return 1; |
| 347 | } |
| 348 | run_cert(crt, *pname, pfn); |
| 349 | X509_free(crt); |
| 350 | ++pname; |
| 351 | } |
| 352 | ++pfn; |
| 353 | } |
| 354 | return errors > 0 ? 1 : 0; |
| 355 | } |