LibVNCServer/LibVNCClient
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rfbssl_gnutls.c
Go to the documentation of this file.
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 
40 void rfbssl_log_func(int level, const char *msg)
41 {
42  rfbErr("SSL: %s", msg);
43 }
44 
45 static void rfbssl_error(const char *msg, int e)
46 {
47  rfbErr("%s: %s (%ld)\n", msg, gnutls_strerror(e), e);
48 }
49 
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 
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
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 
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 
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 
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 
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 
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 
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 
241 int rfbssl_read(rfbClientPtr cl, char *buf, int bufsize)
242 {
243  return __rfbssl_read(cl, buf, bufsize, 0);
244 }
245 
246 int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize)
247 {
248  return __rfbssl_read(cl, buf, bufsize, 1);
249 }
250 
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 
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 }