~ctrlproxy/ctrlproxy/trunk

« back to all changes in this revision

Viewing changes to mods/network-openssl.c

  • Committer: jelmer
  • Date: 2003-10-18 22:02:02 UTC
  • Revision ID: jelmer@samba.org-20031018220202-6801a76318fb4d13
Update

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
        (Based on network-openssl.c from irssi)
 
3
 
 
4
    Copyright (C) 2002 vjt
 
5
    Copyright (C) 2003 Jelmer Vernooij
 
6
 
 
7
    This program is free software; you can redistribute it and/or modify
 
8
    it under the terms of the GNU General Public License as published by
 
9
    the Free Software Foundation; either version 2 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 General Public License for more details.
 
16
 
 
17
    You should have received a copy of the GNU General Public License
 
18
    along with this program; if not, write to the Free Software
 
19
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
20
*/
 
21
 
 
22
#include <glib.h>
 
23
 
 
24
#include <openssl/crypto.h>
 
25
#include <openssl/x509.h>
 
26
#include <openssl/pem.h>
 
27
#include <openssl/ssl.h>
 
28
#include <openssl/err.h>
 
29
 
 
30
/* ssl i/o channel object */
 
31
typedef struct
 
32
{
 
33
        GIOChannel pad;
 
34
        gint fd;
 
35
        GIOChannel *giochan;
 
36
        SSL *ssl;
 
37
        X509 *cert;
 
38
        gboolean isserver;
 
39
} GIOSSLChannel;
 
40
        
 
41
static void irssi_ssl_free(GIOChannel *handle)
 
42
{
 
43
        GIOSSLChannel *chan = (GIOSSLChannel *)handle;
 
44
        g_io_channel_unref(chan->giochan);
 
45
        SSL_free(chan->ssl);
 
46
        g_free(chan);
 
47
}
 
48
 
 
49
static GIOStatus irssi_ssl_errno(gint e)
 
50
{
 
51
        switch(e)
 
52
        {
 
53
                case EINVAL:
 
54
                        return G_IO_STATUS_ERROR;
 
55
                case EINTR:
 
56
                case EAGAIN:
 
57
                        return G_IO_STATUS_AGAIN;
 
58
                default:
 
59
                        return G_IO_STATUS_ERROR;
 
60
        }
 
61
        /*UNREACH*/
 
62
        return G_IO_STATUS_ERROR;
 
63
}
 
64
 
 
65
static GIOStatus irssi_ssl_cert_step(GIOSSLChannel *chan)
 
66
{
 
67
        gint err;
 
68
        switch(err = SSL_do_handshake(chan->ssl))
 
69
        {
 
70
                case 1:
 
71
                        if(!(chan->cert = SSL_get_peer_certificate(chan->ssl)))
 
72
                        {
 
73
                                g_warning("SSL server supplied no certificate");
 
74
                                return G_IO_STATUS_ERROR;
 
75
                        }
 
76
                        return G_IO_STATUS_NORMAL;
 
77
                default:
 
78
                        if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
 
79
                                return G_IO_STATUS_AGAIN;
 
80
                        return irssi_ssl_errno(errno);
 
81
        }
 
82
        /*UNREACH*/
 
83
        return G_IO_STATUS_ERROR;
 
84
}
 
85
 
 
86
static GIOStatus irssi_ssl_read(GIOChannel *handle, gchar *buf, guint len, guint *ret, GError **gerr)
 
87
{
 
88
        GIOSSLChannel *chan = (GIOSSLChannel *)handle;
 
89
        gint err;
 
90
        
 
91
        if(chan->cert == NULL && !chan->isserver)
 
92
        {
 
93
                gint cert_err = irssi_ssl_cert_step(chan);
 
94
                if(cert_err != G_IO_STATUS_NORMAL)
 
95
                        return cert_err;
 
96
        }
 
97
        
 
98
        err = SSL_read(chan->ssl, buf, len);
 
99
        if(err < 0)
 
100
        {
 
101
                *ret = 0;
 
102
                if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
 
103
                        return G_IO_STATUS_AGAIN;
 
104
                return irssi_ssl_errno(errno);
 
105
        } else {
 
106
                *ret = err;
 
107
                if(err == 0) return G_IO_STATUS_EOF;
 
108
                return G_IO_STATUS_NORMAL;
 
109
        }
 
110
        /*UNREACH*/
 
111
        return G_IO_STATUS_ERROR;
 
112
}
 
113
 
 
114
static GIOStatus irssi_ssl_write(GIOChannel *handle, const gchar *buf, gsize len, gsize *ret, GError **gerr)
 
115
{
 
116
        GIOSSLChannel *chan = (GIOSSLChannel *)handle;
 
117
        gint err;
 
118
 
 
119
        if(chan->cert == NULL && !chan->isserver)
 
120
        {
 
121
                gint cert_err = irssi_ssl_cert_step(chan);
 
122
                if(cert_err != G_IO_STATUS_NORMAL)
 
123
                        return cert_err;
 
124
        }
 
125
 
 
126
        err = SSL_write(chan->ssl, (const char *)buf, len);
 
127
        if(err < 0)
 
128
        {
 
129
                *ret = 0;
 
130
                if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
 
131
                        return G_IO_STATUS_AGAIN;
 
132
                return irssi_ssl_errno(errno);
 
133
        }
 
134
        else
 
135
        {
 
136
                *ret = err;
 
137
                return G_IO_STATUS_NORMAL;
 
138
        }
 
139
        /*UNREACH*/
 
140
        return G_IO_STATUS_ERROR;
 
141
}
 
142
 
 
143
static GIOStatus irssi_ssl_seek(GIOChannel *handle, gint64 offset, GSeekType type, GError **gerr)
 
144
{
 
145
        GIOSSLChannel *chan = (GIOSSLChannel *)handle;
 
146
        GIOError e;
 
147
        e = g_io_channel_seek(chan->giochan, offset, type);
 
148
        return (e == G_IO_ERROR_NONE) ? G_IO_STATUS_NORMAL : G_IO_STATUS_ERROR;
 
149
}
 
150
 
 
151
static GIOStatus irssi_ssl_close(GIOChannel *handle, GError **gerr)
 
152
{
 
153
        GIOSSLChannel *chan = (GIOSSLChannel *)handle;
 
154
        g_io_channel_close(chan->giochan);
 
155
 
 
156
        return G_IO_STATUS_NORMAL;
 
157
}
 
158
 
 
159
static GSource *irssi_ssl_create_watch(GIOChannel *handle, GIOCondition cond)
 
160
{
 
161
        GIOSSLChannel *chan = (GIOSSLChannel *)handle;
 
162
 
 
163
        return chan->giochan->funcs->io_create_watch(handle, cond);
 
164
}
 
165
 
 
166
static GIOStatus irssi_ssl_set_flags(GIOChannel *handle, GIOFlags flags, GError **gerr)
 
167
{
 
168
    GIOSSLChannel *chan = (GIOSSLChannel *)handle;
 
169
 
 
170
    return chan->giochan->funcs->io_set_flags(handle, flags, gerr);
 
171
}
 
172
 
 
173
static GIOFlags irssi_ssl_get_flags(GIOChannel *handle)
 
174
{
 
175
    GIOSSLChannel *chan = (GIOSSLChannel *)handle;
 
176
 
 
177
    return chan->giochan->funcs->io_get_flags(handle);
 
178
}
 
179
 
 
180
static GIOFuncs irssi_ssl_channel_funcs = {
 
181
    irssi_ssl_read,
 
182
    irssi_ssl_write,
 
183
    irssi_ssl_seek,
 
184
    irssi_ssl_close,
 
185
    irssi_ssl_create_watch,
 
186
    irssi_ssl_free,
 
187
    irssi_ssl_set_flags,
 
188
    irssi_ssl_get_flags
 
189
};
 
190
 
 
191
static SSL_CTX *ssl_ctx = NULL;
 
192
 
 
193
static gboolean irssi_ssl_init(void)
 
194
{
 
195
        SSL_library_init();
 
196
        SSL_load_error_strings();
 
197
        
 
198
        ssl_ctx = SSL_CTX_new(SSLv23_method());
 
199
        if(!ssl_ctx)
 
200
        {
 
201
                g_error("Initialization of the SSL library failed");
 
202
                return FALSE;
 
203
        }
 
204
 
 
205
        return TRUE;
 
206
 
 
207
}
 
208
 
 
209
gboolean irssi_ssl_set_files(char *certf, char *keyf)
 
210
{
 
211
        if(!ssl_ctx && !irssi_ssl_init())
 
212
                return FALSE;
 
213
 
 
214
        if(SSL_CTX_use_certificate_file(ssl_ctx, certf, SSL_FILETYPE_PEM) <= 0) {
 
215
                g_warning("Can't set SSL certificate file %s!", certf);
 
216
                return FALSE;
 
217
        }
 
218
 
 
219
        if(SSL_CTX_use_PrivateKey_file(ssl_ctx, keyf, SSL_FILETYPE_PEM) <= 0) {
 
220
                g_warning("Can't set SSL private key file %s!", keyf);
 
221
                return FALSE;
 
222
        }
 
223
 
 
224
        if(!SSL_CTX_check_private_key(ssl_ctx)) {
 
225
                g_warning("Private key does not match the certificate public key!");
 
226
                return FALSE;
 
227
        }
 
228
 
 
229
        g_message("Using SSL certificate from %s and SSL key from %s", certf, keyf);
 
230
 
 
231
        return TRUE;
 
232
}
 
233
 
 
234
GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, gboolean server)
 
235
{
 
236
        GIOSSLChannel *chan;
 
237
        GIOChannel *gchan;
 
238
        int err, fd;
 
239
        SSL *ssl;
 
240
        X509 *cert = NULL;
 
241
 
 
242
        g_return_val_if_fail(handle != NULL, NULL);
 
243
        
 
244
        if(!ssl_ctx && !irssi_ssl_init())
 
245
                return NULL;
 
246
 
 
247
        if(!(fd = g_io_channel_unix_get_fd(handle)))
 
248
                return NULL;
 
249
 
 
250
        if(!(ssl = SSL_new(ssl_ctx)))
 
251
        {
 
252
                g_warning("Failed to allocate SSL structure");
 
253
                return NULL;
 
254
        }
 
255
 
 
256
        if(!(err = SSL_set_fd(ssl, fd)))
 
257
        {
 
258
                g_warning("Failed to associate socket to SSL stream");
 
259
                return NULL;
 
260
        }
 
261
        
 
262
        if(server) err = SSL_accept(ssl);
 
263
        else err = SSL_connect(ssl);
 
264
        
 
265
        if(err <= 0)
 
266
        {
 
267
                ERR_print_errors_fp(stderr);
 
268
                return NULL;
 
269
        }
 
270
        else if(!(cert = SSL_get_peer_certificate(ssl)))
 
271
        {
 
272
                if(!server)
 
273
                {
 
274
                        g_warning("SSL %s supplied no certificate", server?"client":"server");
 
275
                        return NULL;
 
276
                }
 
277
        }
 
278
        else
 
279
                X509_free(cert);
 
280
 
 
281
        chan = g_new0(GIOSSLChannel, 1);
 
282
        chan->fd = fd;
 
283
        chan->giochan = handle;
 
284
        chan->ssl = ssl;
 
285
        chan->cert = cert;
 
286
        chan->isserver = server;
 
287
        g_io_channel_ref(handle);
 
288
 
 
289
        gchan = (GIOChannel *)chan;
 
290
        gchan->funcs = &irssi_ssl_channel_funcs;
 
291
        g_io_channel_init(gchan);
 
292
        
 
293
        return gchan;
 
294
}