116 /* As this callback doesn't get auxiliary pointer parameter we |
116 /* As this callback doesn't get auxiliary pointer parameter we |
117 * cannot really use this. However, we can retrieve results later. */ |
117 * cannot really use this. However, we can retrieve results later. */ |
118 return 1; |
118 return 1; |
119 } |
119 } |
120 |
120 |
|
121 static gboolean |
|
122 ssl_match_domain_name (const gchar *server, const gchar *domain) |
|
123 { |
|
124 if (domain[0]=='*' && domain[1]=='.') { |
|
125 /* leftmost part wildcard */ |
|
126 ++domain; |
|
127 |
|
128 if (strchr(domain, '*') != NULL) { |
|
129 /* multiple wildcards not allowed */ |
|
130 return FALSE; |
|
131 } |
|
132 |
|
133 server = strchr(server, '.'); /* eat the leftmost part */ |
|
134 if (server == NULL) { |
|
135 return FALSE; |
|
136 } |
|
137 |
|
138 /* fall thru for wildcard match */ |
|
139 } |
|
140 |
|
141 return (!strcasecmp(server, domain)); |
|
142 } |
|
143 |
121 /* side effect: fills the ssl->fingerprint buffer */ |
144 /* side effect: fills the ssl->fingerprint buffer */ |
122 static gboolean |
145 static gboolean |
123 ssl_verify_certificate (LmSSL *ssl, const gchar *server) |
146 ssl_verify_certificate (LmSSL *ssl, const gchar *server) |
124 { |
147 { |
125 gboolean retval = TRUE; |
148 gboolean retval = TRUE; |
|
149 gboolean match_result = FALSE; |
126 LmSSLBase *base; |
150 LmSSLBase *base; |
127 long verify_res; |
151 long verify_res; |
128 int rc; |
152 int rc; |
129 const EVP_MD *digest = EVP_sha256(); |
153 const EVP_MD *digest = EVP_sha256(); |
130 unsigned int digest_len; |
154 unsigned int digest_len; |
217 crt_subj = X509_get_subject_name(srv_crt); |
241 crt_subj = X509_get_subject_name(srv_crt); |
218 cn = (gchar *) g_malloc0(LM_SSL_CN_MAX + 1); |
242 cn = (gchar *) g_malloc0(LM_SSL_CN_MAX + 1); |
219 |
243 |
220 /* FWB: deprecated call, can only get first entry */ |
244 /* FWB: deprecated call, can only get first entry */ |
221 if (X509_NAME_get_text_by_NID(crt_subj, NID_commonName, cn, LM_SSL_CN_MAX) > 0) { |
245 if (X509_NAME_get_text_by_NID(crt_subj, NID_commonName, cn, LM_SSL_CN_MAX) > 0) { |
222 gchar *domain = cn; |
|
223 |
246 |
224 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, |
247 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, |
225 "%s: server = '%s', cn = '%s'\n", |
248 "%s: server = '%s', cn = '%s'\n", |
226 __FILE__, server, cn); |
249 __FILE__, server, cn); |
227 |
250 |
228 if (domain != NULL) { |
251 if (cn != NULL && ssl_match_domain_name(server, cn) { |
229 |
252 match_result = TRUE; |
230 if ((cn[0] == '*') && (cn[1] == '.')) { |
253 } else { |
231 /* |
254 /* g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: CN does not match server name\n", __FILE__); */ |
232 * FWB: huh? ever tested? |
255 } |
233 * server="sub.domain.tld"; |
256 } else { |
234 * cn="*.domain.tld"; |
257 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, |
235 * domain=strstr(cn, server); ??? |
258 "X509_NAME_get_text_by_NID() failed"); |
236 */ |
259 } |
237 /* domain = strstr (cn, server); */ |
260 |
238 server = strchr(server, '.') + 1; |
261 /* RFC6125: "...However, it is perfectly acceptable for the subject field to be empty, |
239 domain = cn + 2; |
262 * as long as the certificate contains a subject alternative name ("subjectAltName") |
|
263 * extension that includes at least one subjectAltName entry" |
|
264 */ |
|
265 if (!match_result) { |
|
266 /* FWB: CN doesn't match, try SANs */ |
|
267 int subject_alt_names_nb = -1; |
|
268 int san_counter; |
|
269 STACK_OF(GENERAL_NAME) *subject_alt_names = NULL; |
|
270 |
|
271 // Try to extract the names within the SAN extension from the certificate |
|
272 subject_alt_names = X509_get_ext_d2i((X509 *) srv_crt, NID_subject_alt_name, NULL, NULL); |
|
273 if (subject_alt_names != NULL) { |
|
274 |
|
275 // Check each name within the extension |
|
276 subject_alt_names_nb = sk_GENERAL_NAME_num(subject_alt_names); |
|
277 for (san_counter=0; san_counter<subject_alt_names_nb; san_counter++) { |
|
278 const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(subject_alt_names, san_counter); |
|
279 if (current_name->type == GEN_DNS) { |
|
280 // Current name is a DNS name, let's check it, it's ASCII |
|
281 if (ssl_match_domain_name(server, (const char *)current_name->d.dNSName->data)) { |
|
282 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: found SAN '%s' - MATCH\n", __FILE__, current_name->d.dNSName->data); |
|
283 match_result = TRUE; /* break; */ |
|
284 } else { |
|
285 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: found SAN '%s'\n", __FILE__, current_name->d.dNSName->data); |
|
286 } |
|
287 } |
240 } |
288 } |
241 |
289 |
242 if (strncasecmp (server, domain, LM_SSL_CN_MAX) != 0) { |
290 } |
243 /* FWB: CN doesn't match, try SANs */ |
291 |
244 int subject_alt_names_nb = -1; |
292 sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free); |
245 int san_result = 0; |
293 } |
246 int san_counter; |
294 |
247 STACK_OF(GENERAL_NAME) *subject_alt_names = NULL; |
295 if (!match_result) { |
248 |
296 if (base->func (ssl, |
249 /* g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: CN does not match server name\n", __FILE__); */ |
297 LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH, |
250 // Try to extract the names within the SAN extension from the certificate |
298 base->func_data) != LM_SSL_RESPONSE_CONTINUE) { |
251 subject_alt_names = X509_get_ext_d2i((X509 *) srv_crt, NID_subject_alt_name, NULL, NULL); |
299 retval = FALSE; |
252 if (subject_alt_names != NULL) { |
300 } |
253 |
|
254 // Check each name within the extension |
|
255 subject_alt_names_nb = sk_GENERAL_NAME_num(subject_alt_names); |
|
256 for (san_counter=0; san_counter<subject_alt_names_nb; san_counter++) { |
|
257 const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(subject_alt_names, san_counter); |
|
258 if (current_name->type == GEN_DNS) { |
|
259 // Current name is a DNS name, let's check it, it's ASCII |
|
260 if (strcasecmp(server, (char *)current_name->d.dNSName->data) == 0) { |
|
261 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: found SAN '%s' - MATCH\n", __FILE__, current_name->d.dNSName->data); |
|
262 san_result = 1; /* break; */ |
|
263 } else { |
|
264 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: found SAN '%s'\n", __FILE__, current_name->d.dNSName->data); |
|
265 } |
|
266 } |
|
267 } |
|
268 |
|
269 } |
|
270 sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free); |
|
271 if (!san_result) goto cn_and_san_mismatch; |
|
272 } /* SAN */ |
|
273 } else { |
|
274 cn_and_san_mismatch: |
|
275 if (base->func (ssl, |
|
276 LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH, |
|
277 base->func_data) != LM_SSL_RESPONSE_CONTINUE) { |
|
278 retval = FALSE; |
|
279 } |
|
280 } |
|
281 } else { |
|
282 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, |
|
283 "X509_NAME_get_text_by_NID() failed"); |
|
284 } |
301 } |
285 |
302 |
286 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, |
303 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, |
287 "%s:\n\tIssuer: %s\n\tSubject: %s\n\tFor: %s\n", |
304 "%s:\n\tIssuer: %s\n\tSubject: %s\n\tFor: %s\n", |
288 __FILE__, |
305 __FILE__, |