• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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