rfbssl_gnutls.c revision a430b2b5ca4f0967836f5820e8f03adc17fc0a24
1/*
2 * rfbssl_gnutls.c - Secure socket funtions (gnutls version)
3 */
4
5/*
6 *  Copyright (C) 2011 Gernot Tenchio
7 *
8 *  This is free software; you can redistribute it and/or modify
9 *  it under the terms of the GNU General Public License as published by
10 *  the Free Software Foundation; either version 2 of the License, or
11 *  (at your option) any later version.
12 *
13 *  This software is distributed in the hope that it will be useful,
14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *  GNU General Public License for more details.
17 *
18 *  You should have received a copy of the GNU General Public License
19 *  along with this software; if not, write to the Free Software
20 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
21 *  USA.
22 */
23
24#include "rfbssl.h"
25#include <gnutls/gnutls.h>
26#include <errno.h>
27
28struct rfbssl_ctx {
29    char peekbuf[2048];
30    int peeklen;
31    int peekstart;
32    gnutls_session_t session;
33    gnutls_certificate_credentials_t x509_cred;
34    gnutls_dh_params_t dh_params;
35#ifdef I_LIKE_RSA_PARAMS_THAT_MUCH
36    gnutls_rsa_params_t rsa_params;
37#endif
38};
39
40void rfbssl_log_func(int level, const char *msg)
41{
42    rfbErr("SSL: %s", msg);
43}
44
45static void rfbssl_error(const char *msg, int e)
46{
47    rfbErr("%s: %s (%ld)\n", msg, gnutls_strerror(e), e);
48}
49
50static int rfbssl_init_session(struct rfbssl_ctx *ctx, int fd)
51{
52    gnutls_session_t session;
53    int ret;
54
55    if (!GNUTLS_E_SUCCESS == (ret = gnutls_init(&session, GNUTLS_SERVER))) {
56      /* */
57    } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_priority_set_direct(session, "EXPORT", NULL))) {
58      /* */
59    } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, ctx->x509_cred))) {
60      /* */
61    } else {
62      gnutls_session_enable_compatibility_mode(session);
63      gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(uintptr_t)fd);
64      ctx->session = session;
65    }
66    return ret;
67}
68
69static int generate_dh_params(struct rfbssl_ctx *ctx)
70{
71    int ret;
72    if (GNUTLS_E_SUCCESS == (ret = gnutls_dh_params_init(&ctx->dh_params)))
73	ret = gnutls_dh_params_generate2(ctx->dh_params, 1024);
74    return ret;
75}
76
77#ifdef I_LIKE_RSA_PARAMS_THAT_MUCH
78static int generate_rsa_params(struct rfbssl_ctx *ctx)
79{
80    int ret;
81    if (GNUTLS_E_SUCCESS == (ret = gnutls_rsa_params_init(&ctx->rsa_params)))
82	ret = gnutls_rsa_params_generate2(ctx->rsa_params, 512);
83    return ret;
84}
85#endif
86
87struct rfbssl_ctx *rfbssl_init_global(char *key, char *cert)
88{
89    int ret = GNUTLS_E_SUCCESS;
90    struct rfbssl_ctx *ctx = NULL;
91
92    if (NULL == (ctx = malloc(sizeof(struct rfbssl_ctx)))) {
93	ret = GNUTLS_E_MEMORY_ERROR;
94    } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_global_init())) {
95	/* */
96    } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_certificate_allocate_credentials(&ctx->x509_cred))) {
97	/* */
98    } else if ((ret = gnutls_certificate_set_x509_trust_file(ctx->x509_cred, cert, GNUTLS_X509_FMT_PEM)) < 0) {
99	/* */
100    } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_certificate_set_x509_key_file(ctx->x509_cred, cert, key, GNUTLS_X509_FMT_PEM))) {
101	/* */
102    } else if (!GNUTLS_E_SUCCESS == (ret = generate_dh_params(ctx))) {
103	/* */
104#ifdef I_LIKE_RSA_PARAMS_THAT_MUCH
105    } else if (!GNUTLS_E_SUCCESS == (ret = generate_rsa_params(ctx))) {
106	/* */
107#endif
108    } else {
109	gnutls_global_set_log_function(rfbssl_log_func);
110	gnutls_global_set_log_level(1);
111	gnutls_certificate_set_dh_params(ctx->x509_cred, ctx->dh_params);
112	return ctx;
113    }
114
115    free(ctx);
116    return NULL;
117}
118
119int rfbssl_init(rfbClientPtr cl)
120{
121    int ret = -1;
122    struct rfbssl_ctx *ctx;
123    char *keyfile;
124    if (!(keyfile = cl->screen->sslkeyfile))
125	keyfile = cl->screen->sslcertfile;
126
127    if (NULL == (ctx = rfbssl_init_global(keyfile,  cl->screen->sslcertfile))) {
128	/* */
129    } else if (GNUTLS_E_SUCCESS != (ret = rfbssl_init_session(ctx, cl->sock))) {
130	/* */
131    } else {
132	while (GNUTLS_E_SUCCESS != (ret = gnutls_handshake(ctx->session))) {
133	    if (ret == GNUTLS_E_AGAIN)
134		continue;
135	    break;
136	}
137    }
138
139    if (ret != GNUTLS_E_SUCCESS) {
140	rfbssl_error(__func__, ret);
141    } else {
142	cl->sslctx = (rfbSslCtx *)ctx;
143	rfbLog("%s protocol initialized\n", gnutls_protocol_get_name(gnutls_protocol_get_version(ctx->session)));
144    }
145    return ret;
146}
147
148static int rfbssl_do_read(rfbClientPtr cl, char *buf, int bufsize)
149{
150    struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx;
151    int ret;
152
153    while ((ret = gnutls_record_recv(ctx->session, buf, bufsize)) < 0) {
154	if (ret == GNUTLS_E_AGAIN) {
155	    /* continue */
156	} else if (ret == GNUTLS_E_INTERRUPTED) {
157	    /* continue */
158	} else {
159	    break;
160	}
161    }
162
163    if (ret < 0) {
164	rfbssl_error(__func__, ret);
165	errno = EIO;
166	ret = -1;
167    }
168
169    return ret < 0 ? -1 : ret;
170}
171
172int rfbssl_write(rfbClientPtr cl, const char *buf, int bufsize)
173{
174    struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx;
175    int ret;
176
177    while ((ret = gnutls_record_send(ctx->session, buf, bufsize)) < 0) {
178	if (ret == GNUTLS_E_AGAIN) {
179	    /* continue */
180	} else if (ret == GNUTLS_E_INTERRUPTED) {
181	    /* continue */
182	} else {
183	    break;
184	}
185    }
186
187    if (ret < 0)
188	rfbssl_error(__func__, ret);
189
190    return ret;
191}
192
193static void rfbssl_gc_peekbuf(struct rfbssl_ctx *ctx, int bufsize)
194{
195    if (ctx->peekstart) {
196	int spaceleft = sizeof(ctx->peekbuf) - ctx->peeklen - ctx->peekstart;
197	if (spaceleft < bufsize) {
198	    memmove(ctx->peekbuf, ctx->peekbuf + ctx->peekstart, ctx->peeklen);
199	    ctx->peekstart = 0;
200	}
201    }
202}
203
204static int __rfbssl_read(rfbClientPtr cl, char *buf, int bufsize, int peek)
205{
206    int ret = 0;
207    struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx;
208
209    rfbssl_gc_peekbuf(ctx, bufsize);
210
211    if (ctx->peeklen) {
212	/* If we have any peek data, simply return that. */
213	ret = bufsize < ctx->peeklen ? bufsize : ctx->peeklen;
214	memcpy (buf, ctx->peekbuf + ctx->peekstart, ret);
215	if (!peek) {
216	    ctx->peeklen -= ret;
217	    if (ctx->peeklen != 0)
218		ctx->peekstart += ret;
219	    else
220		ctx->peekstart = 0;
221	}
222    }
223
224    if (ret < bufsize) {
225	int n;
226	/* read the remaining data */
227	if ((n = rfbssl_do_read(cl, buf + ret, bufsize - ret)) <= 0) {
228	    rfbErr("rfbssl_%s: %s error\n", __func__, peek ? "peek" : "read");
229	    return n;
230	}
231	if (peek) {
232	    memcpy(ctx->peekbuf + ctx->peekstart + ctx->peeklen, buf + ret, n);
233	    ctx->peeklen += n;
234	}
235	ret += n;
236    }
237
238    return ret;
239}
240
241int rfbssl_read(rfbClientPtr cl, char *buf, int bufsize)
242{
243    return __rfbssl_read(cl, buf, bufsize, 0);
244}
245
246int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize)
247{
248    return __rfbssl_read(cl, buf, bufsize, 1);
249}
250
251int rfbssl_pending(rfbClientPtr cl)
252{
253    struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx;
254    int ret = ctx->peeklen;
255
256    if (ret <= 0)
257	ret = gnutls_record_check_pending(ctx->session);
258
259    return ret;
260}
261
262void rfbssl_destroy(rfbClientPtr cl)
263{
264    struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx;
265    gnutls_bye(ctx->session, GNUTLS_SHUT_WR);
266    gnutls_deinit(ctx->session);
267    gnutls_certificate_free_credentials(ctx->x509_cred);
268}
269