Commit | Line | Data |
---|---|---|
d667da5b IK |
1 | /* |
2 | COPYRIGHT AND PERMISSION NOTICE | |
c235c6ac | 3 | |
d667da5b | 4 | Copyright (c) 1996 - 2010, Daniel Stenberg, <daniel@haxx.se>. |
c235c6ac | 5 | |
d667da5b | 6 | All rights reserved. |
c235c6ac | 7 | |
d667da5b IK |
8 | Permission to use, copy, modify, and distribute this software for any purpose |
9 | with or without fee is hereby granted, provided that the above copyright | |
10 | notice and this permission notice appear in all copies. | |
c235c6ac | 11 | |
d667da5b IK |
12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN | |
15 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
16 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
17 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE | |
18 | OR OTHER DEALINGS IN THE SOFTWARE. | |
c235c6ac | 19 | |
d667da5b IK |
20 | Except as contained in this notice, the name of a copyright holder shall not |
21 | be used in advertising or otherwise to promote the sale, use or other dealings | |
22 | in this Software without prior written authorization of the copyright holder. | |
23 | */ | |
24 | /* simplified to a basic host name check */ | |
d667da5b IK |
25 | #include <openssl/x509_vfy.h> |
26 | #include <openssl/x509v3.h> | |
c235c6ac | 27 | #include <string.h> |
d667da5b IK |
28 | |
29 | #define bool int | |
30 | #define false 0 | |
31 | #define true 1 | |
32 | /** @fn static bool cert_hostcheck(const char *hostname, char *pattern) | |
c235c6ac IK |
33 | * Verifies that the hostname matches against the pattern specified. |
34 | * Handles wildcard patterns and ignores the distinction between upper and lower | |
35 | * case letters. Note: Ported over from ssluse.c in curl (7.1.16) lib Note: | |
36 | * Explicit pattern match disabled as we do not use that for processing node | |
37 | * certificate. Note: No longer ignores the distinction between upper and lower | |
38 | * case letters. Our certificate is generated with lowercase letters. | |
39 | * @return true if matches, false otherwise | |
40 | * @param hostname The hostname we want to check. e.g: vault.trustcommerce.com | |
41 | * @param pattern The pattern we wish to match against. e.g: *.trustcommerce.com | |
42 | */ | |
43 | bool | |
44 | cert_hostcheck(const char* pattern, const char* hostname) | |
d667da5b | 45 | { |
c235c6ac IK |
46 | if (!hostname || !pattern || !*hostname || !*pattern) |
47 | return false; | |
48 | if (!strcmp(hostname, pattern)) | |
49 | return true; | |
50 | return false; | |
d667da5b IK |
51 | } |
52 | /** @fn static bool checkCertificate(X509 *cert, char *host) | |
c235c6ac IK |
53 | * Provides validation of the hostname associated with a certificate. |
54 | * See RFC2818 - Server Identity for an overview of the concept. | |
55 | * This implementation is based off the one found in curl-7.16.1: ssluse.c | |
56 | * but we treat the subjectAltName as a recommendation... so if it fails, | |
57 | * we will proceed to the CN check. | |
58 | * The rationale for this is that we are not always using HTTP (over SSL) | |
59 | * and its more of a certification generation / CA issue and we want | |
60 | * maximum interoperability (as opposed to strict compliance). | |
61 | * @param cert The X509 certificate in question. | |
62 | * @param host The hostname or ip we wish to check. | |
63 | * @return true if matches, false otherwise | |
64 | */ | |
65 | static bool | |
66 | checkCertificate(X509* cert, const char* host) | |
d667da5b | 67 | { |
c235c6ac IK |
68 | int i, j; |
69 | bool matched = false; | |
70 | STACK_OF(GENERAL_NAME) * altnames; | |
71 | unsigned char nulstr[] = { '\0' }; | |
72 | unsigned char* peer_CN = nulstr; | |
73 | X509_NAME* name; | |
74 | ASN1_STRING* tmp; | |
75 | bool status = false; | |
76 | ||
77 | if (!cert || !host) | |
78 | return false; | |
79 | ||
80 | altnames = (STACK_OF(GENERAL_NAME)*)(X509_get_ext_d2i( | |
81 | cert, NID_subject_alt_name, NULL, NULL)); | |
82 | ||
83 | if (altnames != NULL) { | |
84 | int numalts = sk_GENERAL_NAME_num(altnames); | |
85 | for (i = 0; (i < numalts) && (matched == false); i++) { | |
86 | const GENERAL_NAME* check = sk_GENERAL_NAME_value(altnames, i); | |
87 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | |
88 | const char* altptr = (char*)(ASN1_STRING_data(check->d.ia5)); | |
89 | #else | |
90 | const char* altptr = (char*)(ASN1_STRING_get0_data(check->d.ia5)); | |
91 | #endif | |
92 | size_t altlen; | |
93 | switch (check->type) { | |
94 | case GEN_DNS: | |
95 | altlen = ASN1_STRING_length(check->d.ia5); | |
96 | if (altlen == strlen(host) && cert_hostcheck(altptr, host)) | |
97 | matched = true; | |
98 | break; | |
99 | case GEN_IPADD: | |
100 | altlen = ASN1_STRING_length(check->d.ia5); | |
101 | if (altlen == strlen(host) && !memcmp(altptr, host, altlen)) | |
102 | matched = true; | |
103 | break; | |
104 | } | |
105 | } | |
106 | GENERAL_NAMES_free(altnames); | |
107 | if (matched != false) | |
108 | return true; | |
109 | } | |
110 | ||
111 | i = j = -1; | |
112 | ||
113 | name = X509_get_subject_name(cert); | |
114 | if (!name) | |
115 | return false; | |
116 | ||
117 | // get the last CN found in the subject (supposedly its the most distinguished | |
118 | // one) | |
119 | while ((j = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) | |
120 | i = j; | |
121 | ||
122 | if (i < 0) | |
123 | return false; | |
124 | ||
125 | tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); | |
126 | /* workaround for version of openssl < 0.9.7d */ | |
127 | if (tmp && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) { | |
128 | j = ASN1_STRING_length(tmp); | |
129 | if (j >= 0) { | |
130 | peer_CN = (unsigned char*)(OPENSSL_malloc(j + 1)); | |
131 | if (peer_CN) { | |
132 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | |
133 | memcpy(peer_CN, ASN1_STRING_data(tmp), j); | |
134 | #else | |
135 | memcpy(peer_CN, ASN1_STRING_get0_data(tmp), j); | |
136 | #endif | |
137 | peer_CN[j] = '\0'; | |
138 | } | |
139 | } | |
140 | } else { | |
141 | j = ASN1_STRING_to_UTF8(&peer_CN, tmp); | |
142 | } | |
143 | ||
144 | if (peer_CN == nulstr) | |
145 | peer_CN = NULL; | |
146 | ||
147 | if (peer_CN == NULL) | |
148 | return false; // the cn isnt missing in virtually all cases | |
149 | else if (!cert_hostcheck((char*)(peer_CN), host)) | |
150 | status = false; | |
151 | else | |
152 | status = true; | |
153 | ||
154 | if (peer_CN) | |
155 | OPENSSL_free(peer_CN); | |
156 | return status; | |
d667da5b IK |
157 | } |
158 | ||
c235c6ac IK |
159 | int |
160 | TCLinkDefaultValidate(int x, void* cert) | |
d667da5b | 161 | { |
c235c6ac IK |
162 | if (x != 0 || cert == NULL) |
163 | return 0; | |
164 | return !checkCertificate((X509*)cert, "pgw1.trustcommerce.com"); | |
d667da5b | 165 | } |