Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 1 | /* |
| 2 | * osmo-pcap TLS code |
| 3 | * |
| 4 | * (C) 2016 by Holger Hans Peter Freyther <holger@moiji-mobile.com> |
| 5 | * All Rights Reserved |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU Affero General Public License as published by |
| 9 | * the Free Software Foundation; either version 3 of the License, or |
| 10 | * (at your option) any later version. |
| 11 | * |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU Affero General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU Affero General Public License |
| 18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 19 | * |
| 20 | */ |
| 21 | |
| 22 | #include <osmo-pcap/osmo_tls.h> |
| 23 | #include <osmo-pcap/osmo_pcap_client.h> |
Holger Hans Peter Freyther | 9ea4da4 | 2016-09-06 11:38:56 +0200 | [diff] [blame] | 24 | #include <osmo-pcap/osmo_pcap_server.h> |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 25 | #include <osmo-pcap/common.h> |
| 26 | |
| 27 | #include <osmocom/core/write_queue.h> |
| 28 | #include <osmocom/core/talloc.h> |
| 29 | |
| 30 | #include <string.h> |
| 31 | |
| 32 | #define CHECK_RC(rc, str) \ |
| 33 | if (rc != 0) { \ |
| 34 | LOGP(DTLS, LOGL_ERROR, "%s with rc=%d\n", str, rc); \ |
| 35 | exit(1); \ |
| 36 | } |
| 37 | |
Holger Hans Peter Freyther | cf29fd7 | 2016-09-06 20:27:32 +0200 | [diff] [blame] | 38 | static int generate_dh_params(struct osmo_pcap_server *server) |
Holger Hans Peter Freyther | 9ea4da4 | 2016-09-06 11:38:56 +0200 | [diff] [blame] | 39 | { |
Holger Hans Peter Freyther | cf29fd7 | 2016-09-06 20:27:32 +0200 | [diff] [blame] | 40 | int rc; |
Holger Hans Peter Freyther | 9ea4da4 | 2016-09-06 11:38:56 +0200 | [diff] [blame] | 41 | unsigned int bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, |
| 42 | GNUTLS_SEC_PARAM_HIGH); |
| 43 | |
| 44 | LOGP(DTLS, LOGL_NOTICE, "Going to create DH params for %d bits\n", bits); |
Holger Hans Peter Freyther | cf29fd7 | 2016-09-06 20:27:32 +0200 | [diff] [blame] | 45 | |
| 46 | /* allocate it */ |
| 47 | rc = gnutls_dh_params_init (&server->dh_params); |
| 48 | if (rc != GNUTLS_E_SUCCESS) { |
| 49 | LOGP(DTLS, LOGL_ERROR, "Failed to allocate DH params rc=%d\n", rc); |
| 50 | server->dh_params_allocated = false; |
| 51 | return rc; |
| 52 | } |
| 53 | |
| 54 | /* generate and check */ |
| 55 | rc = gnutls_dh_params_generate2 (server->dh_params, bits); |
| 56 | if (rc == GNUTLS_E_SUCCESS) |
| 57 | server->dh_params_allocated = true; |
| 58 | else { |
| 59 | LOGP(DTLS, LOGL_ERROR, "Failed to generate DH params rc=%d\n", rc); |
| 60 | server->dh_params_allocated = false; |
| 61 | gnutls_dh_params_deinit(server->dh_params); |
| 62 | } |
| 63 | return rc; |
| 64 | } |
| 65 | |
| 66 | void osmo_tls_dh_load(struct osmo_pcap_server *server) |
| 67 | { |
| 68 | gnutls_datum_t data; |
| 69 | int rc; |
| 70 | |
| 71 | /* free it before we start */ |
| 72 | if (server->dh_params_allocated) { |
| 73 | gnutls_dh_params_deinit(server->dh_params); |
| 74 | server->dh_params_allocated = false; |
| 75 | } |
| 76 | /* check if we have all data */ |
| 77 | if (!server->tls_dh_pkcs3) { |
| 78 | LOGP(DTLS, LOGL_ERROR, "Can not generate missing pkcs3=%p\n", |
| 79 | server->tls_dh_pkcs3); |
| 80 | return; |
| 81 | } |
| 82 | /* initialize it again */ |
| 83 | rc = gnutls_dh_params_init (&server->dh_params); |
| 84 | if (rc != GNUTLS_E_SUCCESS) { |
| 85 | LOGP(DTLS, LOGL_ERROR, "Failed to allocate DH params rc=%d\n", rc); |
| 86 | server->dh_params_allocated = false; |
| 87 | return; |
| 88 | } |
| 89 | /* load prime and generator */ |
| 90 | rc = gnutls_load_file(server->tls_dh_pkcs3, &data); |
| 91 | if (rc != GNUTLS_E_SUCCESS) { |
| 92 | LOGP(DTLS, LOGL_ERROR, "Failed to load DH params from=%s rc=%d\n", |
| 93 | server->tls_dh_pkcs3, rc); |
| 94 | gnutls_dh_params_deinit(server->dh_params); |
| 95 | return; |
| 96 | } |
| 97 | rc = gnutls_dh_params_import_pkcs3(server->dh_params, &data, GNUTLS_X509_FMT_PEM); |
| 98 | gnutls_free(data.data); |
| 99 | if (rc != GNUTLS_E_SUCCESS) { |
| 100 | LOGP(DTLS, LOGL_ERROR, "Failed to import DH params rc=%d\n", rc); |
| 101 | gnutls_dh_params_deinit(server->dh_params); |
| 102 | return; |
| 103 | } |
| 104 | /* done */ |
| 105 | server->dh_params_allocated = true; |
| 106 | } |
| 107 | |
| 108 | void osmo_tls_dh_generate(struct osmo_pcap_server *server) |
| 109 | { |
| 110 | if (server->dh_params_allocated) |
| 111 | gnutls_dh_params_deinit(server->dh_params); |
| 112 | generate_dh_params(server); |
Holger Hans Peter Freyther | 9ea4da4 | 2016-09-06 11:38:56 +0200 | [diff] [blame] | 113 | } |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 114 | |
| 115 | static int cert_callback(gnutls_session_t tls_session, |
| 116 | const gnutls_datum_t * req_ca_rdn, int nreqs, |
| 117 | const gnutls_pk_algorithm_t * sign_algos, |
| 118 | int sign_algos_length, gnutls_pcert_st ** pcert, |
| 119 | unsigned int *pcert_length, gnutls_privkey_t * pkey) |
| 120 | { |
| 121 | struct osmo_tls_session *sess = gnutls_session_get_ptr(tls_session); |
| 122 | gnutls_certificate_type_t type; |
| 123 | |
| 124 | LOGP(DTLS, LOGL_DEBUG, "cert callback from server\n"); |
| 125 | type = gnutls_certificate_type_get(tls_session); |
| 126 | if (type != GNUTLS_CRT_X509) |
| 127 | return -1; |
| 128 | |
| 129 | *pcert_length = 1; |
| 130 | *pcert = &sess->pcert; |
| 131 | *pkey = sess->privk; |
| 132 | return 0; |
| 133 | } |
| 134 | |
| 135 | static void tls_log_func(int level, const char *str) |
| 136 | { |
| 137 | LOGP(DTLS, LOGL_DEBUG, "GNUtls: |<%d>| %s", level, str); |
| 138 | } |
| 139 | |
| 140 | static int verify_cert_cb(gnutls_session_t session) |
| 141 | { |
| 142 | const char *hostname; |
| 143 | unsigned int status; |
| 144 | int ret; |
| 145 | |
| 146 | hostname = gnutls_session_get_ptr(session); |
| 147 | ret = gnutls_certificate_verify_peers3(session, |
| 148 | hostname, &status); |
| 149 | if (ret != 0) |
| 150 | return GNUTLS_E_CERTIFICATE_ERROR; |
| 151 | if (status != 0) |
| 152 | return GNUTLS_E_CERTIFICATE_ERROR; |
| 153 | return 0; |
| 154 | } |
| 155 | |
| 156 | static void release_keys(struct osmo_tls_session *sess) |
| 157 | { |
| 158 | if (sess->pcert_alloc) { |
| 159 | gnutls_pcert_deinit(&sess->pcert); |
| 160 | sess->pcert_alloc = false; |
| 161 | } |
| 162 | if (sess->privk_alloc) { |
| 163 | gnutls_privkey_deinit(sess->privk); |
| 164 | sess->privk_alloc = false; |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | void osmo_tls_init(void) |
| 169 | { |
| 170 | int rc; |
| 171 | rc = gnutls_global_init(); |
| 172 | CHECK_RC(rc, "init failed"); |
| 173 | gnutls_global_set_log_function(tls_log_func); |
Holger Hans Peter Freyther | cf29fd7 | 2016-09-06 20:27:32 +0200 | [diff] [blame] | 174 | } |
| 175 | |
| 176 | void osmo_tls_server_init(struct osmo_pcap_server *server) |
| 177 | { |
| 178 | int rc; |
| 179 | |
| 180 | if (server->dh_params_allocated) |
| 181 | return; |
| 182 | rc = generate_dh_params(server); |
Holger Hans Peter Freyther | 9ea4da4 | 2016-09-06 11:38:56 +0200 | [diff] [blame] | 183 | CHECK_RC(rc, "dh params failed"); |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 184 | } |
| 185 | |
| 186 | static int need_handshake(struct osmo_tls_session *tls_session) |
| 187 | { |
| 188 | int rc; |
| 189 | |
| 190 | rc = gnutls_handshake(tls_session->session); |
| 191 | if (rc == 0) { |
| 192 | /* handshake is done. start writing if we are allowed to */ |
| 193 | LOGP(DTLS, LOGL_NOTICE, "TLS handshake done.\n"); |
| 194 | if (!llist_empty(&tls_session->wqueue->msg_queue)) |
| 195 | tls_session->wqueue->bfd.when = BSC_FD_WRITE | BSC_FD_READ; |
| 196 | else |
| 197 | tls_session->wqueue->bfd.when = BSC_FD_READ; |
| 198 | tls_session->need_handshake = false; |
| 199 | release_keys(tls_session); |
Holger Hans Peter Freyther | 9ea4da4 | 2016-09-06 11:38:56 +0200 | [diff] [blame] | 200 | if (tls_session->handshake_done) |
| 201 | tls_session->handshake_done(tls_session); |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 202 | } else if (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED) { |
| 203 | LOGP(DTLS, LOGL_DEBUG, "rc=%d will wait for writable again.\n", rc); |
| 204 | } else if (gnutls_error_is_fatal(rc)) { |
| 205 | /* it failed for good.. */ |
| 206 | LOGP(DTLS, LOGL_ERROR, "handshake failed rc=%d str=%s\n", |
| 207 | rc, gnutls_strerror(rc)); |
| 208 | tls_session->wqueue->bfd.when = 0; |
| 209 | tls_session->error(tls_session); |
| 210 | } |
| 211 | return 0; |
| 212 | } |
| 213 | |
| 214 | static int tls_read(struct osmo_tls_session *sess) |
| 215 | { |
| 216 | char buf[1024]; |
| 217 | int rc; |
| 218 | |
Holger Hans Peter Freyther | 9ea4da4 | 2016-09-06 11:38:56 +0200 | [diff] [blame] | 219 | if (sess->read) |
| 220 | return sess->read(sess); |
| 221 | |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 222 | memset(buf, 0, sizeof(buf)); |
| 223 | rc = gnutls_record_recv(sess->session, buf, sizeof(buf) - 1); |
| 224 | return rc; |
| 225 | } |
| 226 | |
| 227 | static int tls_write(struct osmo_tls_session *sess) |
| 228 | { |
| 229 | int rc; |
| 230 | sess->wqueue->bfd.when &= ~BSC_FD_WRITE; |
| 231 | |
| 232 | if (llist_empty(&sess->wqueue->msg_queue)) |
| 233 | return 0; |
| 234 | |
| 235 | if (sess->need_resend) { |
| 236 | rc = gnutls_record_send(sess->session, NULL, 0); |
| 237 | } else { |
| 238 | struct msgb *msg; |
| 239 | msg = (struct msgb *) sess->wqueue->msg_queue.next; |
| 240 | rc = gnutls_record_send(sess->session, msg->data, msg->len); |
| 241 | } |
| 242 | |
| 243 | if (rc > 0) { |
| 244 | sess->wqueue->current_length -= 1; |
| 245 | sess->need_resend = false; |
| 246 | struct msgb *msg = msgb_dequeue(&sess->wqueue->msg_queue); |
| 247 | msgb_free(msg); |
| 248 | } else if (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN) { |
| 249 | sess->need_resend = true; |
| 250 | } else if (gnutls_error_is_fatal(rc)) { |
| 251 | return rc; |
| 252 | } |
| 253 | |
| 254 | if (sess->need_resend || !llist_empty(&sess->wqueue->msg_queue)) |
| 255 | sess->wqueue->bfd.when |= BSC_FD_WRITE; |
| 256 | return rc; |
| 257 | } |
| 258 | |
| 259 | int osmo_tls_client_bfd_cb(struct osmo_fd *fd, unsigned what) |
| 260 | { |
| 261 | struct osmo_tls_session *sess = fd->data; |
| 262 | |
| 263 | if (sess->need_handshake) |
| 264 | return need_handshake(sess); |
| 265 | |
| 266 | if (what & BSC_FD_READ) { |
| 267 | int rc = tls_read(sess); |
| 268 | if (rc <= 0) { |
| 269 | sess->error(sess); |
| 270 | return rc; |
| 271 | } |
| 272 | } |
| 273 | if (what & BSC_FD_WRITE) { |
| 274 | int rc = tls_write(sess); |
| 275 | if (rc < 0) { |
| 276 | sess->error(sess); |
| 277 | return rc; |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | return 0; |
| 282 | } |
| 283 | |
Holger Hans Peter Freyther | bdda28b | 2016-11-08 14:32:45 +0100 | [diff] [blame] | 284 | static int load_keys(struct osmo_pcap_client_conn *conn) |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 285 | { |
Holger Hans Peter Freyther | bdda28b | 2016-11-08 14:32:45 +0100 | [diff] [blame] | 286 | struct osmo_tls_session *sess = &conn->tls_session; |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 287 | gnutls_datum_t data; |
| 288 | int rc; |
| 289 | |
Holger Hans Peter Freyther | bdda28b | 2016-11-08 14:32:45 +0100 | [diff] [blame] | 290 | if (!conn->tls_client_cert || !conn->tls_client_key) { |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 291 | LOGP(DTLS, LOGL_DEBUG, "Skipping x509 client cert %p %p\n", |
Holger Hans Peter Freyther | bdda28b | 2016-11-08 14:32:45 +0100 | [diff] [blame] | 292 | conn->tls_client_cert, conn->tls_client_key); |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 293 | return 0; |
| 294 | } |
| 295 | |
| 296 | |
Holger Hans Peter Freyther | bdda28b | 2016-11-08 14:32:45 +0100 | [diff] [blame] | 297 | rc = gnutls_load_file(conn->tls_client_cert, &data); |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 298 | if (rc < 0) { |
| 299 | LOGP(DTLS, LOGL_ERROR, "Failed to load file=%s rc=%d\n", |
Holger Hans Peter Freyther | bdda28b | 2016-11-08 14:32:45 +0100 | [diff] [blame] | 300 | conn->tls_client_cert, rc); |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 301 | return -1; |
| 302 | } |
| 303 | rc = gnutls_pcert_import_x509_raw(&sess->pcert, &data, GNUTLS_X509_FMT_PEM, 0); |
| 304 | gnutls_free(data.data); |
| 305 | if (rc < 0) { |
| 306 | LOGP(DTLS, LOGL_ERROR, "Failed to import file=%s rc=%d\n", |
Holger Hans Peter Freyther | bdda28b | 2016-11-08 14:32:45 +0100 | [diff] [blame] | 307 | conn->tls_client_cert, rc); |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 308 | return -1; |
| 309 | } |
| 310 | sess->pcert_alloc = true; |
| 311 | |
| 312 | /* copied to RAM.. nothing we can do about it */ |
Holger Hans Peter Freyther | bdda28b | 2016-11-08 14:32:45 +0100 | [diff] [blame] | 313 | rc = gnutls_load_file(conn->tls_client_key, &data); |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 314 | if (rc < 0) { |
| 315 | LOGP(DTLS, LOGL_ERROR, "Failed to load file=%s rc=%d\n", |
Holger Hans Peter Freyther | bdda28b | 2016-11-08 14:32:45 +0100 | [diff] [blame] | 316 | conn->tls_client_key, rc); |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 317 | return -1; |
| 318 | } |
| 319 | gnutls_privkey_init(&sess->privk); |
| 320 | rc = gnutls_privkey_import_x509_raw(sess->privk, &data, GNUTLS_X509_FMT_PEM, NULL, 0); |
| 321 | gnutls_free(data.data); |
| 322 | if (rc < 0) { |
| 323 | LOGP(DTLS, LOGL_ERROR, "Failed to load file=%s rc=%d\n", |
Holger Hans Peter Freyther | bdda28b | 2016-11-08 14:32:45 +0100 | [diff] [blame] | 324 | conn->tls_client_key, rc); |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 325 | release_keys(sess); |
| 326 | return -1; |
| 327 | } |
| 328 | sess->privk_alloc = true; |
| 329 | return 0; |
| 330 | } |
| 331 | |
Holger Hans Peter Freyther | 9ea4da4 | 2016-09-06 11:38:56 +0200 | [diff] [blame] | 332 | size_t osmo_tls_pending(struct osmo_tls_session *sess) |
| 333 | { |
| 334 | return gnutls_record_check_pending(sess->session); |
| 335 | } |
| 336 | |
| 337 | bool osmo_tls_init_server_session(struct osmo_pcap_conn *conn, |
| 338 | struct osmo_pcap_server *server) |
| 339 | { |
| 340 | struct osmo_tls_session *sess = &conn->tls_session; |
| 341 | struct osmo_wqueue *wq = &conn->rem_wq; |
| 342 | int rc; |
| 343 | |
| 344 | gnutls_global_set_log_level(server->tls_log_level); |
| 345 | |
| 346 | memset(sess, 0, sizeof(*sess)); |
| 347 | sess->in_use = sess->anon_alloc = sess->cert_alloc = false; |
| 348 | rc = gnutls_init(&sess->session, GNUTLS_SERVER | GNUTLS_NONBLOCK); |
| 349 | if (rc != GNUTLS_E_SUCCESS) { |
| 350 | LOGP(DTLS, LOGL_ERROR, "gnutls_init failed with rc=%d\n", rc); |
| 351 | return false; |
| 352 | } |
| 353 | gnutls_session_set_ptr(sess->session, sess); |
| 354 | sess->in_use = true; |
| 355 | |
| 356 | /* use default or string */ |
| 357 | if (server->tls_priority) { |
| 358 | const char *err; |
| 359 | rc = gnutls_priority_set_direct(sess->session, server->tls_priority, &err); |
| 360 | } else { |
| 361 | rc = gnutls_set_default_priority(sess->session); |
| 362 | } |
| 363 | |
| 364 | if (rc != GNUTLS_E_SUCCESS) { |
| 365 | LOGP(DTLS, LOGL_ERROR, "def prio failed with rc=%d\n", rc); |
| 366 | osmo_tls_release(sess); |
| 367 | return false; |
| 368 | } |
| 369 | |
| 370 | /* allow username/password operation */ |
| 371 | rc = gnutls_anon_allocate_server_credentials(&sess->anon_serv_cred); |
| 372 | if (rc != GNUTLS_E_SUCCESS) { |
| 373 | LOGP(DTLS, LOGL_ERROR, "Failed to allocate anon cred rc=%d\n", rc); |
| 374 | osmo_tls_release(sess); |
| 375 | return false; |
| 376 | } |
| 377 | sess->anon_serv_alloc = true; |
| 378 | |
| 379 | /* x509 certificate handling */ |
| 380 | rc = gnutls_certificate_allocate_credentials(&sess->cert_cred); |
| 381 | if (rc != GNUTLS_E_SUCCESS) { |
| 382 | LOGP(DTLS, LOGL_ERROR, "Failed to allocate x509 cred rc=%d\n", rc); |
| 383 | osmo_tls_release(sess); |
| 384 | return false; |
| 385 | } |
| 386 | sess->cert_alloc = true; |
| 387 | |
| 388 | /* set the credentials now */ |
Holger Hans Peter Freyther | cf29fd7 | 2016-09-06 20:27:32 +0200 | [diff] [blame] | 389 | if (server->dh_params_allocated) { |
| 390 | gnutls_anon_set_server_dh_params(sess->anon_serv_cred, server->dh_params); |
| 391 | gnutls_certificate_set_dh_params(sess->cert_cred, server->dh_params); |
| 392 | } |
| 393 | |
| 394 | if (server->tls_allow_anon) |
| 395 | gnutls_credentials_set(sess->session, GNUTLS_CRD_ANON, sess->anon_serv_cred); |
| 396 | if (server->tls_allow_x509) |
| 397 | gnutls_credentials_set(sess->session, GNUTLS_CRD_CERTIFICATE, sess->cert_cred); |
Holger Hans Peter Freyther | 9ea4da4 | 2016-09-06 11:38:56 +0200 | [diff] [blame] | 398 | |
| 399 | if (server->tls_capath) { |
| 400 | rc = gnutls_certificate_set_x509_trust_file( |
| 401 | sess->cert_cred, server->tls_capath, GNUTLS_X509_FMT_PEM); |
| 402 | if (rc != GNUTLS_E_SUCCESS) { |
| 403 | LOGP(DTLS, LOGL_ERROR, "Failed to load capath from path=%s rc=%d\n", |
| 404 | server->tls_capath, rc); |
| 405 | osmo_tls_release(sess); |
| 406 | return false; |
| 407 | } |
| 408 | } |
| 409 | |
Holger Hans Peter Freyther | cf29fd7 | 2016-09-06 20:27:32 +0200 | [diff] [blame] | 410 | if (server->tls_crlfile) { |
| 411 | rc = gnutls_certificate_set_x509_crl_file( |
| 412 | sess->cert_cred, server->tls_crlfile, GNUTLS_X509_FMT_PEM); |
| 413 | if (rc != GNUTLS_E_SUCCESS) { |
| 414 | LOGP(DTLS, LOGL_ERROR, "Failed to load crlfile from path=%s rc=%d\n", |
| 415 | server->tls_crlfile, rc); |
| 416 | osmo_tls_release(sess); |
| 417 | return false; |
| 418 | } |
Holger Hans Peter Freyther | 9ea4da4 | 2016-09-06 11:38:56 +0200 | [diff] [blame] | 419 | } |
Holger Hans Peter Freyther | cf29fd7 | 2016-09-06 20:27:32 +0200 | [diff] [blame] | 420 | |
| 421 | if (server->tls_server_cert && server->tls_server_key) { |
| 422 | rc = gnutls_certificate_set_x509_key_file( |
| 423 | sess->cert_cred, server->tls_server_cert, server->tls_server_key, |
| 424 | GNUTLS_X509_FMT_PEM); |
| 425 | if (rc != GNUTLS_E_SUCCESS) { |
| 426 | LOGP(DTLS, LOGL_ERROR, "Failed to load crt/key from path=%s/%s rc=%d\n", |
| 427 | server->tls_server_cert, server->tls_server_key, rc); |
| 428 | osmo_tls_release(sess); |
| 429 | return false; |
| 430 | } |
| 431 | } |
Holger Hans Peter Freyther | 9ea4da4 | 2016-09-06 11:38:56 +0200 | [diff] [blame] | 432 | |
| 433 | #warning "TODO client certificates" |
| 434 | |
| 435 | gnutls_transport_set_int(sess->session, wq->bfd.fd); |
| 436 | gnutls_handshake_set_timeout(sess->session, |
| 437 | GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); |
| 438 | wq->bfd.cb = osmo_tls_client_bfd_cb; |
| 439 | wq->bfd.data = sess; |
| 440 | wq->bfd.when = BSC_FD_READ | BSC_FD_WRITE; |
| 441 | sess->need_handshake = true; |
| 442 | sess->wqueue = wq; |
| 443 | return true; |
| 444 | } |
| 445 | |
Holger Hans Peter Freyther | 13f397c | 2016-11-08 10:33:03 +0100 | [diff] [blame] | 446 | bool osmo_tls_init_client_session(struct osmo_pcap_client_conn *client) |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 447 | { |
| 448 | struct osmo_tls_session *sess = &client->tls_session; |
| 449 | struct osmo_wqueue *wq = &client->wqueue; |
| 450 | unsigned int status; |
| 451 | int rc; |
| 452 | |
| 453 | gnutls_global_set_log_level(client->tls_log_level); |
| 454 | |
| 455 | memset(sess, 0, sizeof(*sess)); |
| 456 | sess->in_use = sess->anon_alloc = sess->cert_alloc = false; |
| 457 | rc = gnutls_init(&sess->session, GNUTLS_CLIENT | GNUTLS_NONBLOCK); |
| 458 | if (rc != GNUTLS_E_SUCCESS) { |
| 459 | LOGP(DTLS, LOGL_ERROR, "gnutls_init failed with rc=%d\n", rc); |
| 460 | return false; |
| 461 | } |
| 462 | gnutls_session_set_ptr(sess->session, sess); |
| 463 | sess->in_use = true; |
| 464 | |
| 465 | /* use default or string */ |
| 466 | if (client->tls_priority) { |
| 467 | const char *err; |
| 468 | rc = gnutls_priority_set_direct(sess->session, client->tls_priority, &err); |
| 469 | } else { |
| 470 | rc = gnutls_set_default_priority(sess->session); |
| 471 | } |
| 472 | |
| 473 | if (rc != GNUTLS_E_SUCCESS) { |
| 474 | LOGP(DTLS, LOGL_ERROR, "def prio failed with rc=%d\n", rc); |
| 475 | osmo_tls_release(sess); |
| 476 | return false; |
| 477 | } |
| 478 | |
| 479 | /* allow username/password operation */ |
| 480 | rc = gnutls_anon_allocate_client_credentials(&sess->anon_cred); |
| 481 | if (rc != GNUTLS_E_SUCCESS) { |
| 482 | LOGP(DTLS, LOGL_ERROR, "Failed to allocate anon cred rc=%d\n", rc); |
| 483 | osmo_tls_release(sess); |
| 484 | return false; |
| 485 | } |
| 486 | sess->anon_alloc = true; |
| 487 | |
| 488 | /* x509 certificate handling */ |
| 489 | rc = gnutls_certificate_allocate_credentials(&sess->cert_cred); |
| 490 | if (rc != GNUTLS_E_SUCCESS) { |
| 491 | LOGP(DTLS, LOGL_ERROR, "Failed to allocate x509 cred rc=%d\n", rc); |
| 492 | osmo_tls_release(sess); |
| 493 | return false; |
| 494 | } |
| 495 | sess->cert_alloc = true; |
| 496 | |
| 497 | /* set the credentials now */ |
| 498 | gnutls_credentials_set(sess->session, GNUTLS_CRD_ANON, sess->anon_cred); |
| 499 | gnutls_credentials_set(sess->session, GNUTLS_CRD_CERTIFICATE, sess->cert_cred); |
| 500 | |
| 501 | if (client->tls_capath) { |
| 502 | rc = gnutls_certificate_set_x509_trust_file( |
| 503 | sess->cert_cred, client->tls_capath, GNUTLS_X509_FMT_PEM); |
| 504 | if (rc != GNUTLS_E_SUCCESS) { |
| 505 | LOGP(DTLS, LOGL_ERROR, "Failed to load capath from path=%s rc=%d\n", |
| 506 | client->tls_capath, rc); |
| 507 | osmo_tls_release(sess); |
| 508 | return false; |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | if (load_keys(client) != 0) { |
| 513 | osmo_tls_release(sess); |
| 514 | return false; |
| 515 | } |
| 516 | |
| 517 | gnutls_certificate_set_retrieve_function2(sess->cert_cred, cert_callback); |
| 518 | |
| 519 | /* set the hostname if we have one */ |
| 520 | if (client->tls_hostname) |
| 521 | gnutls_server_name_set(sess->session, GNUTLS_NAME_DNS, |
| 522 | client->tls_hostname, strlen(client->tls_hostname)); |
| 523 | |
| 524 | /* do the verification */ |
| 525 | if (client->tls_verify) { |
| 526 | gnutls_certificate_set_verify_function(sess->cert_cred, verify_cert_cb); |
| 527 | gnutls_certificate_verify_peers3(sess->session, client->tls_hostname, &status); |
| 528 | } else |
| 529 | LOGP(DTLS, LOGL_NOTICE, "Not going to validate certs as configured\n"); |
| 530 | |
| 531 | gnutls_transport_set_int(sess->session, wq->bfd.fd); |
| 532 | gnutls_handshake_set_timeout(sess->session, |
| 533 | GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); |
| 534 | wq->bfd.cb = osmo_tls_client_bfd_cb; |
| 535 | wq->bfd.data = sess; |
| 536 | wq->bfd.when = BSC_FD_READ | BSC_FD_WRITE; |
| 537 | sess->need_handshake = true; |
| 538 | sess->wqueue = wq; |
| 539 | return true; |
| 540 | } |
| 541 | |
| 542 | void osmo_tls_release(struct osmo_tls_session *session) |
| 543 | { |
| 544 | if (!session->in_use) |
| 545 | return; |
| 546 | |
| 547 | gnutls_deinit(session->session); |
| 548 | |
| 549 | release_keys(session); |
| 550 | |
| 551 | if (session->anon_alloc) |
| 552 | gnutls_anon_free_client_credentials(session->anon_cred); |
Holger Hans Peter Freyther | 9ea4da4 | 2016-09-06 11:38:56 +0200 | [diff] [blame] | 553 | if (session->anon_serv_alloc) |
| 554 | gnutls_anon_free_server_credentials(session->anon_serv_cred); |
Holger Hans Peter Freyther | c266796 | 2016-08-25 23:07:44 +0200 | [diff] [blame] | 555 | if (session->cert_alloc) |
| 556 | gnutls_certificate_free_credentials(session->cert_cred); |
| 557 | session->in_use = false; |
| 558 | } |