Implement disable_hostname_check config option.
[libradsec.git] / lib / tls.c
1 /* Copyright 2010-2013 NORDUnet A/S. All rights reserved.
2    See LICENSE for licensing information. */
3
4 #if defined HAVE_CONFIG_H
5 #include <config.h>
6 #endif
7
8 #include <stdlib.h>
9 #include <assert.h>
10 #include <openssl/ssl.h>
11 #include <openssl/err.h>
12 #include <openssl/bn.h>
13 #include <openssl/x509v3.h>
14 #include <radsec/radsec.h>
15 #include <radsec/radsec-impl.h>
16
17 #include <regex.h>
18 #include "radsecproxy/list.h"
19 #include "radsecproxy/radsecproxy.h"
20
21 static struct tls *
22 _get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm)
23 {
24   struct tls *c = rs_malloc (conn->ctx, sizeof (struct tls));
25
26   if (c)
27     {
28       memset (c, 0, sizeof (struct tls));
29       /* TODO: Make sure old radsecproxy code doesn't free these all
30          of a sudden, or strdup them.  */
31       c->name = realm->name;
32       c->cacertfile = realm->cacertfile;
33       c->cacertpath = NULL;     /* NYI */
34       c->certfile = realm->certfile;
35       c->certkeyfile = realm->certkeyfile;
36       c->certkeypwd = NULL;     /* NYI */
37       c->cacheexpiry = 0;       /* NYI */
38       c->crlcheck = 0;          /* NYI */
39       c->policyoids = (char **) NULL; /* NYI */
40     }
41     else
42       rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL);
43
44   return c;
45 }
46
47 #if defined RS_ENABLE_TLS_PSK
48 static unsigned int
49 psk_client_cb (SSL *ssl,
50                const char *hint,
51                char *identity,
52                unsigned int max_identity_len,
53                unsigned char *psk,
54                unsigned int max_psk_len)
55 {
56   struct rs_connection *conn = NULL;
57   struct rs_credentials *cred = NULL;
58
59   conn = SSL_get_ex_data (ssl, 0);
60   assert (conn != NULL);
61   cred = conn->active_peer->realm->transport_cred;
62   assert (cred != NULL);
63   /* NOTE: Ignoring identity hint from server.  */
64
65   if (strlen (cred->identity) + 1 > max_identity_len)
66     {
67       rs_err_conn_push (conn, RSE_CRED, "PSK identity longer than max %d",
68                         max_identity_len - 1);
69       return 0;
70     }
71   strcpy (identity, cred->identity);
72
73   switch (cred->secret_encoding)
74     {
75     case RS_KEY_ENCODING_UTF8:
76       cred->secret_len = strlen (cred->secret);
77       if (cred->secret_len > max_psk_len)
78         {
79           rs_err_conn_push (conn, RSE_CRED, "PSK secret longer than max %d",
80                             max_psk_len);
81           return 0;
82         }
83       memcpy (psk, cred->secret, cred->secret_len);
84       break;
85     case RS_KEY_ENCODING_ASCII_HEX:
86       {
87         BIGNUM *bn = NULL;
88
89         if (BN_hex2bn (&bn, cred->secret) == 0)
90           {
91             rs_err_conn_push (conn, RSE_CRED, "Unable to convert pskhexstr");
92             if (bn != NULL)
93               BN_clear_free (bn);
94             return 0;
95           }
96         if ((unsigned int) BN_num_bytes (bn) > max_psk_len)
97           {
98             rs_err_conn_push (conn, RSE_CRED, "PSK secret longer than max %d",
99                              max_psk_len);
100             BN_clear_free (bn);
101             return 0;
102           }
103         cred->secret_len = BN_bn2bin (bn, psk);
104         BN_clear_free (bn);
105       }
106       break;
107     default:
108       assert (!"unknown psk encoding");
109     }
110
111   return cred->secret_len;
112 }
113 #endif  /* RS_ENABLE_TLS_PSK */
114
115 int
116 rs_tls_init (struct rs_connection *conn)
117 {
118   struct rs_context *ctx = NULL;
119   struct tls *tlsconf = NULL;
120   SSL_CTX *ssl_ctx = NULL;
121   SSL *ssl = NULL;
122   unsigned long sslerr = 0;
123
124   assert (conn->ctx);
125   ctx = conn->ctx;
126
127   tlsconf = _get_tlsconf (conn, conn->active_peer->realm);
128   if (!tlsconf)
129     return -1;
130   ssl_ctx = tlsgetctx (RAD_TLS, tlsconf);
131   if (!ssl_ctx)
132     {
133       for (sslerr = ERR_get_error (); sslerr; sslerr = ERR_get_error ())
134          rs_err_conn_push_fl (conn, RSE_SSLERR, __FILE__, __LINE__,
135                               ERR_error_string (sslerr, NULL));
136       return -1;
137     }
138   ssl = SSL_new (ssl_ctx);
139   if (!ssl)
140     {
141       for (sslerr = ERR_get_error (); sslerr; sslerr = ERR_get_error ())
142         rs_err_conn_push_fl (conn, RSE_SSLERR, __FILE__, __LINE__,
143                              ERR_error_string (sslerr, NULL));
144       return -1;
145     }
146
147 #if defined RS_ENABLE_TLS_PSK
148   if (conn->active_peer->realm->transport_cred != NULL)
149     {
150       SSL_set_psk_client_callback (ssl, psk_client_cb);
151       SSL_set_ex_data (ssl, 0, conn);
152     }
153 #endif  /* RS_ENABLE_TLS_PSK */
154
155   conn->tls_ctx = ssl_ctx;
156   conn->tls_ssl = ssl;
157   rs_free (ctx, tlsconf);
158   return RSE_OK;
159 }
160
161 /* draft-ietf-radext-radsec-11.txt
162
163        *  Certificate validation MUST include the verification rules as
164           per [RFC5280].
165
166        *  Implementations SHOULD indicate their acceptable Certification
167           Authorities as per section 7.4.4 (server side) and x.y.z
168           ["Trusted CA Indication"] (client side) of [RFC5246] (see
169           Section 3.2)
170
171        *  Implementations SHOULD allow to configure a list of acceptable
172           certificates, identified via certificate fingerprint.  When a
173           fingerprint configured, the fingerprint is prepended with an
174           ASCII label identifying the hash function followed by a colon.
175           Implementations MUST support SHA-1 as the hash algorithm and
176           use the ASCII label "sha-1" to identify the SHA-1 algorithm.
177           The length of a SHA-1 hash is 20 bytes and the length of the
178           corresponding fingerprint string is 65 characters.  An example
179           certificate fingerprint is: sha-
180           1:E1:2D:53:2B:7C:6B:8A:29:A2:76:C8:64:36:0B:08:4B:7A:F1:9E:9D
181
182        *  Peer validation always includes a check on whether the locally
183           configured expected DNS name or IP address of the server that
184           is contacted matches its presented certificate.  DNS names and
185           IP addresses can be contained in the Common Name (CN) or
186           subjectAltName entries.  For verification, only one of these
187           entries is to be considered.  The following precedence
188           applies: for DNS name validation, subjectAltName:DNS has
189           precedence over CN; for IP address validation, subjectAltName:
190           iPAddr has precedence over CN.
191
192        *  Implementations SHOULD allow to configure a set of acceptable
193           values for subjectAltName:URI.
194  */
195 int
196 tls_verify_cert (struct rs_connection *conn)
197 {
198   int err = 0;
199   int success = 0;
200   X509 *peer_cert = NULL;
201   struct in6_addr addr;
202   const char *hostname = NULL;
203
204   assert (conn->active_peer->conn == conn);
205   assert (conn->active_peer->hostname != NULL);
206   hostname = conn->active_peer->hostname;
207
208   /* verifytlscert() performs basic verification as described by
209      OpenSSL VERIFY(1), i.e. verification of the certificate chain.  */
210   peer_cert = verifytlscert (conn->tls_ssl);
211   if (peer_cert == NULL)
212     {
213       err = rs_err_conn_push (conn, RSE_SSLERR,
214                               "basic certificate validation failed");
215       goto out;
216     }
217
218   if (inet_pton (AF_INET, hostname, &addr))
219     success = (subjectaltnameaddr (peer_cert, AF_INET, &addr) == 1);
220   else if (inet_pton (AF_INET6, hostname, &addr))
221     success = (subjectaltnameaddr (peer_cert, AF_INET6, &addr) == 1);
222   else
223     success = (subjectaltnameregexp (peer_cert, GEN_DNS, hostname, NULL) == 1);
224
225   if (!success)
226     success = (cnregexp (peer_cert, hostname, NULL) == 1);
227
228   if (conn->realm->disable_hostname_check)
229     success = 1;
230   if (!success)
231     err = rs_err_conn_push (conn, RSE_CERT, "server certificate doesn't "
232                             "match configured hostname \"%s\"", hostname);
233
234  out:
235   if (peer_cert != NULL)
236     X509_free (peer_cert);
237   return err;
238 }