📄 ssl.c
字号:
/* * jabberd - Jabber Open Source Server * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney, * Ryan Eatmon, Robert Norris * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA *//** * this plugin implements the traditional SSL "wrappermode" streams and * STARTTLS extension documented in xmpp-core */#include "sx.h"#ifdef HAVE_SSL#include "ssl.h"static void _sx_ssl_starttls_notify_proceed(sx_t s, void *arg) { _sx_debug(ZONE, "preparing for starttls"); _sx_reset(s); /* start listening */ sx_server_init(s, s->flags | SX_SSL_WRAPPER);}static int _sx_ssl_process(sx_t s, sx_plugin_t p, nad_t nad) { int flags; char *ns = NULL, *to = NULL, *from = NULL, *version = NULL; sx_error_t sxe; /* not interested if we're a server and we never offered it */ if(s->type == type_SERVER && !(s->flags & SX_SSL_STARTTLS_OFFER)) return 1; /* only want tls packets */ if(NAD_ENS(nad, 0) < 0 || NAD_NURI_L(nad, NAD_ENS(nad, 0)) != strlen(uri_TLS) || strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_TLS, strlen(uri_TLS)) != 0) return 1; /* starttls from client */ if(s->type == type_SERVER) { if(NAD_ENAME_L(nad, 0) == 8 || strncmp(NAD_ENAME(nad, 0), "starttls", 8) == 0) { nad_free(nad); /* can't go on if we've been here before */ if(s->ssf > 0) { _sx_debug(ZONE, "starttls requested on already encrypted channel, dropping packet"); return 0; } _sx_debug(ZONE, "starttls requested, setting up"); /* go ahead */ jqueue_push(s->wbufq, _sx_buffer_new("<proceed xmlns='" uri_TLS "'/>", strlen(uri_TLS) + 19, _sx_ssl_starttls_notify_proceed, NULL), 0); s->want_write = 1; /* handled the packet */ return 0; } } else if(s->type == type_CLIENT) { /* kick off the handshake */ if(NAD_ENAME_L(nad, 0) == 7 || strncmp(NAD_ENAME(nad, 0), "proceed", 7) == 0) { nad_free(nad); /* save interesting bits */ flags = s->flags; if(s->ns != NULL) ns = strdup(s->ns); if(s->req_to != NULL) to = strdup(s->req_to); if(s->req_from != NULL) from = strdup(s->req_from); if(s->req_version != NULL) version = strdup(s->req_version); /* reset state */ _sx_reset(s); _sx_debug(ZONE, "server ready for ssl, starting"); /* second time round */ sx_client_init(s, flags | SX_SSL_WRAPPER, ns, to, from, version); /* free bits */ if(ns != NULL) free(ns); if(to != NULL) free(to); if(from != NULL) free(from); if(version != NULL) free(version); return 0; } /* busted server */ if(NAD_ENAME_L(nad, 0) == 7 || strncmp(NAD_ENAME(nad, 0), "failure", 7) == 0) { nad_free(nad); /* free the pemfile arg */ if(s->plugin_data[p->index] != NULL) free(s->plugin_data[p->index]); _sx_debug(ZONE, "server can't handle ssl, business as usual"); _sx_gen_error(sxe, SX_ERR_STARTTLS_FAILURE, "STARTTLS failure", "Server was unable to prepare for the TLS handshake"); _sx_event(s, event_ERROR, (void *) &sxe); return 0; } } _sx_debug(ZONE, "unknown starttls namespace element '%.*s', dropping packet", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0)); nad_free(nad); return 0;}static void _sx_ssl_features(sx_t s, sx_plugin_t p, nad_t nad) { int ns; /* if the session is already encrypted, or the app told us not to, then we don't offer anything */ if(s->state > state_STREAM || s->ssf > 0 || !(s->flags & SX_SSL_STARTTLS_OFFER)) return; _sx_debug(ZONE, "offering starttls"); ns = nad_add_namespace(nad, uri_TLS, NULL); nad_append_elem(nad, ns, "starttls", 1); if(s->flags & SX_SSL_STARTTLS_REQUIRE) nad_append_elem(nad, ns, "required", 2);}static int _sx_ssl_handshake(sx_t s, _sx_ssl_conn_t sc) { int ret, err; char *errstring; sx_error_t sxe; /* work on establishing the channel */ while(!SSL_is_init_finished(sc->ssl)) { _sx_debug(ZONE, "secure channel not established, handshake in progress"); /* we can't handshake if they want to read, but there's nothing to read */ if(sc->last_state == SX_SSL_STATE_WANT_READ && BIO_pending(sc->rbio) == 0) return 0; /* more handshake */ if(s->type == type_CLIENT) ret = SSL_connect(sc->ssl); else ret = SSL_accept(sc->ssl); /* check if we're done */ if(ret == 1) { _sx_debug(ZONE, "secure channel established"); sc->last_state = SX_SSL_STATE_NONE; s->ssf = SSL_get_cipher_bits(sc->ssl, NULL); _sx_debug(ZONE, "using cipher %s (%d bits)", SSL_get_cipher_name(sc->ssl), s->ssf); return 1; } /* error checking */ else if(ret <= 0) { err = SSL_get_error(sc->ssl, ret); if(err == SSL_ERROR_WANT_READ) sc->last_state = SX_SSL_STATE_WANT_READ; else if(err == SSL_ERROR_WANT_WRITE) sc->last_state = SX_SSL_STATE_WANT_WRITE; else { /* fatal error */ sc->last_state = SX_SSL_STATE_ERROR; errstring = ERR_error_string(ERR_get_error(), NULL); _sx_debug(ZONE, "openssl error: %s", errstring); /* throw an error */ _sx_gen_error(sxe, SX_ERR_SSL, "SSL handshake error", errstring); _sx_event(s, event_ERROR, (void *) &sxe); _sx_error(s, stream_err_INTERNAL_SERVER_ERROR, errstring); _sx_close(s); /* !!! drop queue */ return -1; } } } return 1;}static int _sx_ssl_wio(sx_t s, sx_plugin_t p, sx_buf_t buf) { _sx_ssl_conn_t sc = (_sx_ssl_conn_t) s->plugin_data[p->index]; int est, ret, err; sx_buf_t wbuf; char *errstring; sx_error_t sxe; /* sanity */ if(sc->last_state == SX_SSL_STATE_ERROR) return -2; _sx_debug(ZONE, "in _sx_ssl_wio"); /* queue the buffer */ if(buf->len > 0) { _sx_debug(ZONE, "queueing buffer for write"); jqueue_push(sc->wq, _sx_buffer_new(buf->data, buf->len, buf->notify, buf->notify_arg), 0); _sx_buffer_clear(buf); buf->notify = NULL; buf->notify_arg = NULL; } /* handshake */ est = _sx_ssl_handshake(s, sc); if(est < 0) return -2; /* fatal error */ /* channel established, do some real writing */ wbuf = NULL; if(est > 0 && jqueue_size(sc->wq) > 0) { _sx_debug(ZONE, "preparing queued buffer for write"); wbuf = jqueue_pull(sc->wq); ret = SSL_write(sc->ssl, wbuf->data, wbuf->len); if(ret <= 0) { /* something's wrong */ _sx_debug(ZONE, "write failed, requeuing buffer"); /* requeue the buffer */ jqueue_push(sc->wq, wbuf, (sc->wq->front != NULL) ? sc->wq->front->priority + 1 : 0); /* error checking */ err = SSL_get_error(sc->ssl, ret); if(err == SSL_ERROR_ZERO_RETURN) { /* ssl channel closed, we're done */ _sx_close(s); } if(err == SSL_ERROR_WANT_READ) { /* we'll be renegotiating next time */ _sx_debug(ZONE, "renegotiation started"); sc->last_state = SX_SSL_STATE_WANT_READ; } else { sc->last_state = SX_SSL_STATE_ERROR; /* something very bad */ errstring = ERR_error_string(ERR_get_error(), NULL); _sx_debug(ZONE, "openssl error: %s", errstring); /* throw an error */ _sx_gen_error(sxe, SX_ERR_SSL, "SSL handshake error", errstring); _sx_event(s, event_ERROR, (void *) &sxe); _sx_error(s, stream_err_INTERNAL_SERVER_ERROR, errstring); _sx_close(s); /* !!! drop queue */ return -2; /* fatal */ } } } /* prepare the buffer with stuff to write */ if(BIO_pending(sc->wbio) > 0) { int bytes_pending = BIO_pending(sc->wbio); assert(buf->len == 0); _sx_buffer_alloc_margin(buf, 0, bytes_pending); BIO_read(sc->wbio, buf->data, bytes_pending); buf->len += bytes_pending; /* restore notify and clean up */ if(wbuf != NULL) { buf->notify = wbuf->notify; buf->notify_arg = wbuf->notify_arg; _sx_buffer_free(wbuf); } _sx_debug(ZONE, "prepared %d ssl bytes for write", buf->len); } /* flag if we want to read */ if(sc->last_state == SX_SSL_STATE_WANT_READ || sc->last_state == SX_SSL_STATE_NONE) s->want_read = 1; return 1;}static int _sx_ssl_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) { _sx_ssl_conn_t sc = (_sx_ssl_conn_t) s->plugin_data[p->index]; int est, ret, err, pending; char *errstring; sx_error_t sxe; /* sanity */ if(sc->last_state == SX_SSL_STATE_ERROR) return -1; _sx_debug(ZONE, "in _sx_ssl_rio"); /* move the data into the ssl read buffer */ if(buf->len > 0) { _sx_debug(ZONE, "loading %d bytes into ssl read buffer", buf->len); BIO_write(sc->rbio, buf->data, buf->len); _sx_buffer_clear(buf); } /* handshake */ est = _sx_ssl_handshake(s, sc); if(est < 0) return -1; /* fatal error */ /* channel is up, slurp up the read buffer */ if(est > 0) { pending = SSL_pending(sc->ssl); if(pending == 0) pending = BIO_pending(sc->rbio); /* get it all */ while((pending = SSL_pending(sc->ssl)) > 0 || (pending = BIO_pending(sc->rbio)) > 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -