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
28 struct 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
rfbssl_log_func(int level,const char * msg)40 void rfbssl_log_func(int level, const char *msg)
41 {
42 rfbErr("SSL: %s", msg);
43 }
44
rfbssl_error(const char * msg,int e)45 static void rfbssl_error(const char *msg, int e)
46 {
47 rfbErr("%s: %s (%ld)\n", msg, gnutls_strerror(e), e);
48 }
49
rfbssl_init_session(struct rfbssl_ctx * ctx,int fd)50 static 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
generate_dh_params(struct rfbssl_ctx * ctx)69 static 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
generate_rsa_params(struct rfbssl_ctx * ctx)78 static 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
rfbssl_init_global(char * key,char * cert)87 struct 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
rfbssl_init(rfbClientPtr cl)119 int 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
rfbssl_do_read(rfbClientPtr cl,char * buf,int bufsize)148 static 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
rfbssl_write(rfbClientPtr cl,const char * buf,int bufsize)172 int 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
rfbssl_gc_peekbuf(struct rfbssl_ctx * ctx,int bufsize)193 static 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
__rfbssl_read(rfbClientPtr cl,char * buf,int bufsize,int peek)204 static 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
rfbssl_read(rfbClientPtr cl,char * buf,int bufsize)241 int rfbssl_read(rfbClientPtr cl, char *buf, int bufsize)
242 {
243 return __rfbssl_read(cl, buf, bufsize, 0);
244 }
245
rfbssl_peek(rfbClientPtr cl,char * buf,int bufsize)246 int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize)
247 {
248 return __rfbssl_read(cl, buf, bufsize, 1);
249 }
250
rfbssl_pending(rfbClientPtr cl)251 int 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
rfbssl_destroy(rfbClientPtr cl)262 void 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