]> granicus.if.org Git - apache/blob - modules/ssl/ssl_util_ssl.c
mod_ssl namespacing: Make SSL_X509_getIDs a static function inside the
[apache] / modules / ssl / ssl_util_ssl.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*                      _             _
18  *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
19  * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
20  * | | | | | | (_) | (_| |   \__ \__ \ |
21  * |_| |_| |_|\___/ \__,_|___|___/___/_|
22  *                      |_____|
23  *  ssl_util_ssl.c
24  *  Additional Utility Functions for OpenSSL
25  */
26
27 #include "ssl_private.h"
28
29 /*  _________________________________________________________________
30 **
31 **  Additional High-Level Functions for OpenSSL
32 **  _________________________________________________________________
33 */
34
35 /* we initialize this index at startup time
36  * and never write to it at request time,
37  * so this static is thread safe.
38  * also note that OpenSSL increments at static variable when
39  * SSL_get_ex_new_index() is called, so we _must_ do this at startup.
40  */
41 static int app_data2_idx = -1;
42
43 void modssl_init_app_data2_idx(void)
44 {
45     int i;
46
47     if (app_data2_idx > -1) {
48         return;
49     }
50
51     /* we _do_ need to call this twice */
52     for (i = 0; i <= 1; i++) {
53         app_data2_idx =
54             SSL_get_ex_new_index(0,
55                                  "Second Application Data for SSL",
56                                  NULL, NULL, NULL);
57     }
58 }
59
60 void *modssl_get_app_data2(SSL *ssl)
61 {
62     return (void *)SSL_get_ex_data(ssl, app_data2_idx);
63 }
64
65 void modssl_set_app_data2(SSL *ssl, void *arg)
66 {
67     SSL_set_ex_data(ssl, app_data2_idx, (char *)arg);
68     return;
69 }
70
71 /*  _________________________________________________________________
72 **
73 **  High-Level Private Key Loading
74 **  _________________________________________________________________
75 */
76
77 EVP_PKEY *modssl_read_privatekey(const char* filename, EVP_PKEY **key, pem_password_cb *cb, void *s)
78 {
79     EVP_PKEY *rc;
80     BIO *bioS;
81     BIO *bioF;
82
83     /* 1. try PEM (= DER+Base64+headers) */
84     if ((bioS=BIO_new_file(filename, "r")) == NULL)
85         return NULL;
86     rc = PEM_read_bio_PrivateKey(bioS, key, cb, s);
87     BIO_free(bioS);
88
89     if (rc == NULL) {
90         /* 2. try DER+Base64 */
91         if ((bioS = BIO_new_file(filename, "r")) == NULL)
92             return NULL;
93
94         if ((bioF = BIO_new(BIO_f_base64())) == NULL) {
95             BIO_free(bioS);
96             return NULL;
97         }
98         bioS = BIO_push(bioF, bioS);
99         rc = d2i_PrivateKey_bio(bioS, NULL);
100         BIO_free_all(bioS);
101
102         if (rc == NULL) {
103             /* 3. try plain DER */
104             if ((bioS = BIO_new_file(filename, "r")) == NULL)
105                 return NULL;
106             rc = d2i_PrivateKey_bio(bioS, NULL);
107             BIO_free(bioS);
108         }
109     }
110     if (rc != NULL && key != NULL) {
111         if (*key != NULL)
112             EVP_PKEY_free(*key);
113         *key = rc;
114     }
115     return rc;
116 }
117
118 /*  _________________________________________________________________
119 **
120 **  Smart shutdown
121 **  _________________________________________________________________
122 */
123
124 int modssl_smart_shutdown(SSL *ssl)
125 {
126     int i;
127     int rc;
128     int flush;
129
130     /*
131      * Repeat the calls, because SSL_shutdown internally dispatches through a
132      * little state machine. Usually only one or two interation should be
133      * needed, so we restrict the total number of restrictions in order to
134      * avoid process hangs in case the client played bad with the socket
135      * connection and OpenSSL cannot recognize it.
136      */
137     rc = 0;
138     flush = !(SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN);
139     for (i = 0; i < 4 /* max 2x pending + 2x data = 4 */; i++) {
140         rc = SSL_shutdown(ssl);
141         if (rc >= 0 && flush && (SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN)) {
142             /* Once the close notity is sent through the output filters,
143              * ensure it is flushed through the socket.
144              */
145             if (BIO_flush(SSL_get_wbio(ssl)) <= 0) {
146                 rc = -1;
147                 break;
148             }
149             flush = 0;
150         }
151         if (rc != 0)
152             break;
153     }
154     return rc;
155 }
156
157 /*  _________________________________________________________________
158 **
159 **  Certificate Checks
160 **  _________________________________________________________________
161 */
162
163 /* retrieve basic constraints ingredients */
164 BOOL modssl_X509_getBC(X509 *cert, int *ca, int *pathlen)
165 {
166     BASIC_CONSTRAINTS *bc;
167     BIGNUM *bn = NULL;
168     char *cp;
169
170     bc = X509_get_ext_d2i(cert, NID_basic_constraints, NULL, NULL);
171     if (bc == NULL)
172         return FALSE;
173     *ca = bc->ca;
174     *pathlen = -1 /* unlimited */;
175     if (bc->pathlen != NULL) {
176         if ((bn = ASN1_INTEGER_to_BN(bc->pathlen, NULL)) == NULL) {
177             BASIC_CONSTRAINTS_free(bc);
178             return FALSE;
179         }
180         if ((cp = BN_bn2dec(bn)) == NULL) {
181             BN_free(bn);
182             BASIC_CONSTRAINTS_free(bc);
183             return FALSE;
184         }
185         *pathlen = atoi(cp);
186         OPENSSL_free(cp);
187         BN_free(bn);
188     }
189     BASIC_CONSTRAINTS_free(bc);
190     return TRUE;
191 }
192
193 /* convert an ASN.1 string to a UTF-8 string (escaping control characters) */
194 static char *convert_asn1_to_utf8(apr_pool_t *p, ASN1_STRING *asn1str)
195 {
196     char *result = NULL;
197     BIO *bio;
198     int len;
199
200     if ((bio = BIO_new(BIO_s_mem())) == NULL)
201         return NULL;
202
203     ASN1_STRING_print_ex(bio, asn1str, ASN1_STRFLGS_ESC_CTRL|
204                                        ASN1_STRFLGS_UTF8_CONVERT);
205     len = BIO_pending(bio);
206     if (len > 0) {
207         result = apr_palloc(p, len+1);
208         len = BIO_read(bio, result, len);
209         result[len] = NUL;
210     }
211     BIO_free(bio);
212     return result;
213 }
214
215 /* convert a NAME_ENTRY to UTF8 string */
216 char *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne)
217 {
218     char *result = convert_asn1_to_utf8(p, X509_NAME_ENTRY_get_data(xsne));
219     ap_xlate_proto_from_ascii(result, len);
220     return result;
221 }
222
223 /*
224  * convert an X509_NAME to an RFC 2253 formatted string, optionally truncated
225  * to maxlen characters (specify a maxlen of 0 for no length limit)
226  */
227 char *modssl_X509_NAME_to_string(apr_pool_t *p, X509_NAME *dn, int maxlen)
228 {
229     char *result = NULL;
230     BIO *bio;
231     int len;
232
233     if ((bio = BIO_new(BIO_s_mem())) == NULL)
234         return NULL;
235     X509_NAME_print_ex(bio, dn, 0, XN_FLAG_RFC2253);
236     len = BIO_pending(bio);
237     if (len > 0) {
238         result = apr_palloc(p, (maxlen > 0) ? maxlen+1 : len+1);
239         if (maxlen > 0 && maxlen < len) {
240             len = BIO_read(bio, result, maxlen);
241             if (maxlen > 2) {
242                 /* insert trailing ellipsis if there's enough space */
243                 apr_snprintf(result + maxlen - 3, 4, "...");
244             }
245         } else {
246             len = BIO_read(bio, result, len);
247         }
248         result[len] = NUL;
249     }
250     BIO_free(bio);
251
252     return result;
253 }
254
255 /* 
256  * Return an array of subjectAltName entries of type "type". If idx is -1,
257  * return all entries of the given type, otherwise return an array consisting
258  * of the n-th occurrence of that type only. Currently supported types:
259  * GEN_EMAIL (rfc822Name)
260  * GEN_DNS (dNSName)
261  */
262 BOOL modssl_X509_getSAN(apr_pool_t *p, X509 *x509, int type, int idx,
263                         apr_array_header_t **entries)
264 {
265     STACK_OF(GENERAL_NAME) *names;
266
267     if (!x509 || (type < GEN_OTHERNAME) || (type > GEN_RID) || (idx < -1) ||
268         !(*entries = apr_array_make(p, 0, sizeof(char *)))) {
269         *entries = NULL;
270         return FALSE;
271     }
272
273     if ((names = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL))) {
274         int i, n = 0;
275         GENERAL_NAME *name;
276         const char *utf8str;
277
278         for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
279             name = sk_GENERAL_NAME_value(names, i);
280             if (name->type == type) {
281                 if ((idx == -1) || (n == idx)) {
282                     switch (type) {
283                     case GEN_EMAIL:
284                     case GEN_DNS:
285                         utf8str = convert_asn1_to_utf8(p, name->d.ia5);
286                         if (utf8str) {
287                             APR_ARRAY_PUSH(*entries, const char *) = utf8str;
288                         }
289                         break;
290                     default:
291                         /*
292                          * Not implemented right now:
293                          * GEN_OTHERNAME (otherName)
294                          * GEN_X400 (x400Address)
295                          * GEN_DIRNAME (directoryName)
296                          * GEN_EDIPARTY (ediPartyName)
297                          * GEN_URI (uniformResourceIdentifier)
298                          * GEN_IPADD (iPAddress)
299                          * GEN_RID (registeredID)
300                          */
301                         break;
302                     }
303                 }
304                 if ((idx != -1) && (n++ > idx))
305                    break;
306             }
307         }
308
309         sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
310     }
311
312     return apr_is_empty_array(*entries) ? FALSE : TRUE;
313 }
314
315 /* return an array of (RFC 6125 coined) DNS-IDs and CN-IDs in a certificate */
316 static BOOL getIDs(apr_pool_t *p, X509 *x509, apr_array_header_t **ids)
317 {
318     X509_NAME *subj;
319     int i = -1;
320
321     /* First, the DNS-IDs (dNSName entries in the subjectAltName extension) */
322     if (!x509 ||
323         (modssl_X509_getSAN(p, x509, GEN_DNS, -1, ids) == FALSE && !*ids)) {
324         *ids = NULL;
325         return FALSE;
326     }
327
328     /* Second, the CN-IDs (commonName attributes in the subject DN) */
329     subj = X509_get_subject_name(x509);
330     while ((i = X509_NAME_get_index_by_NID(subj, NID_commonName, i)) != -1) {
331         APR_ARRAY_PUSH(*ids, const char *) = 
332             modssl_X509_NAME_ENTRY_to_string(p, X509_NAME_get_entry(subj, i));
333     }
334
335     return apr_is_empty_array(*ids) ? FALSE : TRUE;
336 }
337
338 /* 
339  * Check if a certificate matches for a particular name, by iterating over its
340  * DNS-IDs and CN-IDs (RFC 6125), optionally with basic wildcard matching.
341  * If server_rec is non-NULL, some (debug/trace) logging is enabled.
342  */
343 BOOL SSL_X509_match_name(apr_pool_t *p, X509 *x509, const char *name,
344                          BOOL allow_wildcard, server_rec *s)
345 {
346     BOOL matched = FALSE;
347     apr_array_header_t *ids;
348
349     /*
350      * At some day in the future, this might be replaced with X509_check_host()
351      * (available in OpenSSL 1.0.2 and later), but two points should be noted:
352      * 1) wildcard matching in X509_check_host() might yield different
353      *    results (by default, it supports a broader set of patterns, e.g.
354      *    wildcards in non-initial positions);
355      * 2) we lose the option of logging each DNS- and CN-ID (until a match
356      *    is found).
357      */
358
359     if (getIDs(p, x509, &ids)) {
360         const char *cp;
361         int i;
362         char **id = (char **)ids->elts;
363         BOOL is_wildcard;
364
365         for (i = 0; i < ids->nelts; i++) {
366             if (!id[i])
367                 continue;
368
369             /*
370              * Determine if it is a wildcard ID - we're restrictive
371              * in the sense that we require the wildcard character to be
372              * THE left-most label (i.e., the ID must start with "*.")
373              */
374             is_wildcard = (*id[i] == '*' && *(id[i]+1) == '.') ? TRUE : FALSE;
375
376             /*
377              * If the ID includes a wildcard character (and the caller is
378              * allowing wildcards), check if it matches for the left-most
379              * DNS label - i.e., the wildcard character is not allowed
380              * to match a dot. Otherwise, try a simple string compare.
381              */
382             if ((allow_wildcard == TRUE && is_wildcard == TRUE &&
383                  (cp = ap_strchr_c(name, '.')) && !strcasecmp(id[i]+1, cp)) ||
384                 !strcasecmp(id[i], name)) {
385                 matched = TRUE;
386             }
387
388             if (s) {
389                 ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
390                              "[%s] SSL_X509_match_name: expecting name '%s', "
391                              "%smatched by ID '%s'",
392                              (mySrvConfig(s))->vhost_id, name,
393                              matched == TRUE ? "" : "NOT ", id[i]);
394             }
395
396             if (matched == TRUE) {
397                 break;
398             }
399         }
400
401     }
402
403     if (s) {
404         ssl_log_xerror(SSLLOG_MARK, APLOG_DEBUG, 0, p, s, x509,
405                        APLOGNO(02412) "[%s] Cert %s for name '%s'",
406                        (mySrvConfig(s))->vhost_id,
407                        matched == TRUE ? "matches" : "does not match",
408                        name);
409     }
410
411     return matched;
412 }
413
414 /*  _________________________________________________________________
415 **
416 **  Low-Level CA Certificate Loading
417 **  _________________________________________________________________
418 */
419
420 BOOL SSL_X509_INFO_load_file(apr_pool_t *ptemp,
421                              STACK_OF(X509_INFO) *sk,
422                              const char *filename)
423 {
424     BIO *in;
425
426     if (!(in = BIO_new(BIO_s_file()))) {
427         return FALSE;
428     }
429
430     if (BIO_read_filename(in, filename) <= 0) {
431         BIO_free(in);
432         return FALSE;
433     }
434
435     ERR_clear_error();
436
437     PEM_X509_INFO_read_bio(in, sk, NULL, NULL);
438
439     BIO_free(in);
440
441     return TRUE;
442 }
443
444 BOOL SSL_X509_INFO_load_path(apr_pool_t *ptemp,
445                              STACK_OF(X509_INFO) *sk,
446                              const char *pathname)
447 {
448     /* XXX: this dir read code is exactly the same as that in
449      * ssl_engine_init.c, only the call to handle the fullname is different,
450      * should fold the duplication.
451      */
452     apr_dir_t *dir;
453     apr_finfo_t dirent;
454     apr_int32_t finfo_flags = APR_FINFO_TYPE|APR_FINFO_NAME;
455     const char *fullname;
456     BOOL ok = FALSE;
457
458     if (apr_dir_open(&dir, pathname, ptemp) != APR_SUCCESS) {
459         return FALSE;
460     }
461
462     while ((apr_dir_read(&dirent, finfo_flags, dir)) == APR_SUCCESS) {
463         if (dirent.filetype == APR_DIR) {
464             continue; /* don't try to load directories */
465         }
466
467         fullname = apr_pstrcat(ptemp,
468                                pathname, "/", dirent.name,
469                                NULL);
470
471         if (SSL_X509_INFO_load_file(ptemp, sk, fullname)) {
472             ok = TRUE;
473         }
474     }
475
476     apr_dir_close(dir);
477
478     return ok;
479 }
480
481 /*  _________________________________________________________________
482 **
483 **  Custom (EC)DH parameter support
484 **  _________________________________________________________________
485 */
486
487 DH *ssl_dh_GetParamFromFile(const char *file)
488 {
489     DH *dh = NULL;
490     BIO *bio;
491
492     if ((bio = BIO_new_file(file, "r")) == NULL)
493         return NULL;
494     dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
495     BIO_free(bio);
496     return (dh);
497 }
498
499 #ifdef HAVE_ECC
500 EC_GROUP *ssl_ec_GetParamFromFile(const char *file)
501 {
502     EC_GROUP *group = NULL;
503     BIO *bio;
504
505     if ((bio = BIO_new_file(file, "r")) == NULL)
506         return NULL;
507     group = PEM_read_bio_ECPKParameters(bio, NULL, NULL, NULL);
508     BIO_free(bio);
509     return (group);
510 }
511 #endif
512
513 /*  _________________________________________________________________
514 **
515 **  Extra Server Certificate Chain Support
516 **  _________________________________________________________________
517 */
518
519 /*
520  * Read a file that optionally contains the server certificate in PEM
521  * format, possibly followed by a sequence of CA certificates that
522  * should be sent to the peer in the SSL Certificate message.
523  */
524 int SSL_CTX_use_certificate_chain(
525     SSL_CTX *ctx, char *file, int skipfirst, pem_password_cb *cb)
526 {
527     BIO *bio;
528     X509 *x509;
529     unsigned long err;
530     int n;
531
532     if ((bio = BIO_new(BIO_s_file_internal())) == NULL)
533         return -1;
534     if (BIO_read_filename(bio, file) <= 0) {
535         BIO_free(bio);
536         return -1;
537     }
538     /* optionally skip a leading server certificate */
539     if (skipfirst) {
540         if ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) == NULL) {
541             BIO_free(bio);
542             return -1;
543         }
544         X509_free(x509);
545     }
546     /* free a perhaps already configured extra chain */
547 #ifdef OPENSSL_NO_SSL_INTERN
548     SSL_CTX_clear_extra_chain_certs(ctx);
549 #else
550     if (ctx->extra_certs != NULL) {
551         sk_X509_pop_free((STACK_OF(X509) *)ctx->extra_certs, X509_free);
552         ctx->extra_certs = NULL;
553     }
554 #endif
555     /* create new extra chain by loading the certs */
556     n = 0;
557     while ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) != NULL) {
558         if (!SSL_CTX_add_extra_chain_cert(ctx, x509)) {
559             X509_free(x509);
560             BIO_free(bio);
561             return -1;
562         }
563         n++;
564     }
565     /* Make sure that only the error is just an EOF */
566     if ((err = ERR_peek_error()) > 0) {
567         if (!(   ERR_GET_LIB(err) == ERR_LIB_PEM
568               && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
569             BIO_free(bio);
570             return -1;
571         }
572         while (ERR_get_error() > 0) ;
573     }
574     BIO_free(bio);
575     return n;
576 }
577
578 /*  _________________________________________________________________
579 **
580 **  Session Stuff
581 **  _________________________________________________________________
582 */
583
584 char *SSL_SESSION_id2sz(unsigned char *id, int idlen,
585                         char *str, int strsize)
586 {
587     if (idlen > SSL_MAX_SSL_SESSION_ID_LENGTH)
588         idlen = SSL_MAX_SSL_SESSION_ID_LENGTH;
589         
590     /* We must ensure not to process more than what would fit in the
591      * destination buffer, including terminating NULL */
592     if (idlen > (strsize-1) / 2)
593         idlen = (strsize-1) / 2;
594
595     ap_bin2hex(id, idlen, str);
596
597     return str;
598 }