1 /***************************************************************************
2 begin : Wed Apr 28 2010
3 copyright : (C) 2010, 2016 by Martin Preuss
4 email : martin@libchipcard.de
5
6 ***************************************************************************
7 * *
8 * This library is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU Lesser General Public *
10 * License as published by the Free Software Foundation; either *
11 * version 2.1 of the License, or (at your option) any later version. *
12 * *
13 * This library 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 GNU *
16 * Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public *
19 * License along with this library; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 * *
23 ***************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include <config.h>
27 #endif
28
29 #define DISABLE_DEBUGLOG
30
31 /*#define GWEN_TLS_DEBUG*/
32
33 /* #define GWEN_TLS_USE_OLD_CODE */
34
35 #include "syncio_tls_p.h"
36 #include "i18n_l.h"
37
38 #include <gwenhywfar/misc.h>
39 #include <gwenhywfar/debug.h>
40 #include <gwenhywfar/gui.h>
41 #include <gwenhywfar/gui.h>
42 #include <gwenhywfar/pathmanager.h>
43 #include <gwenhywfar/directory.h>
44 #include <gwenhywfar/gwenhywfar.h>
45 #include <gwenhywfar/text.h>
46
47 #include <assert.h>
48 #include <errno.h>
49 #include <string.h>
50 #include <stdlib.h>
51
52 #include <gnutls/gnutls.h>
53 #include <gnutls/x509.h>
54 #include <gcrypt.h>
55
56
57
58 GWEN_INHERIT(GWEN_SYNCIO, GWEN_SYNCIO_TLS)
59
60
61 #ifndef OS_WIN32
62 const char *SYNCIO_TLS_SYSTEM_CERTFILES[]= {
63 "/etc/ssl/certs/ca-certificates.crt",
64 "/etc/ssl/ca-bundle.pem",
65 NULL
66 };
67 #endif
68
69
70
71
GWEN_SyncIo_Tls_new(GWEN_SYNCIO * baseIo)72 GWEN_SYNCIO *GWEN_SyncIo_Tls_new(GWEN_SYNCIO *baseIo)
73 {
74 GWEN_SYNCIO *sio;
75 GWEN_SYNCIO_TLS *xio;
76
77 assert(baseIo);
78 sio=GWEN_SyncIo_new(GWEN_SYNCIO_TLS_TYPE, baseIo);
79 GWEN_NEW_OBJECT(GWEN_SYNCIO_TLS, xio);
80 GWEN_INHERIT_SETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio, xio, GWEN_SyncIo_Tls_FreeData);
81
82 /* preset data */
83 xio->checkCertFn=GWEN_SyncIo_Tls_Internal_CheckCert;
84
85 /* set virtual functions */
86 GWEN_SyncIo_SetConnectFn(sio, GWEN_SyncIo_Tls_Connect);
87 GWEN_SyncIo_SetDisconnectFn(sio, GWEN_SyncIo_Tls_Disconnect);
88 GWEN_SyncIo_SetReadFn(sio, GWEN_SyncIo_Tls_Read);
89 GWEN_SyncIo_SetWriteFn(sio, GWEN_SyncIo_Tls_Write);
90
91 return sio;
92 }
93
94
95
GWEN_SyncIo_Tls_FreeData(GWEN_UNUSED void * bp,void * p)96 void GWENHYWFAR_CB GWEN_SyncIo_Tls_FreeData(GWEN_UNUSED void *bp, void *p)
97 {
98 GWEN_SYNCIO_TLS *xio;
99
100 xio=(GWEN_SYNCIO_TLS *) p;
101 free(xio->localCertFile);
102 free(xio->localKeyFile);
103 free(xio->localTrustFile);
104 free(xio->dhParamFile);
105 free(xio->hostName);
106 GWEN_SslCertDescr_free(xio->peerCertDescr);
107 GWEN_FREE_OBJECT(xio);
108 }
109
110
111
GWEN_SyncIo_Tls_SetCheckCertFn(GWEN_SYNCIO * sio,GWEN_SIO_TLS_CHECKCERT_FN f)112 GWEN_SIO_TLS_CHECKCERT_FN GWEN_SyncIo_Tls_SetCheckCertFn(GWEN_SYNCIO *sio, GWEN_SIO_TLS_CHECKCERT_FN f)
113 {
114 GWEN_SYNCIO_TLS *xio;
115 GWEN_SIO_TLS_CHECKCERT_FN oldF;
116
117 assert(sio);
118 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
119 assert(xio);
120
121 oldF=xio->checkCertFn;
122 xio->checkCertFn=f;
123 return oldF;
124 }
125
126
127
GWEN_SyncIo_Tls_Internal_CheckCert(GWEN_SYNCIO * sio,const GWEN_SSLCERTDESCR * cert)128 GWENHYWFAR_CB int GWEN_SyncIo_Tls_Internal_CheckCert(GWEN_SYNCIO *sio,
129 const GWEN_SSLCERTDESCR *cert)
130 {
131 GWEN_SYNCIO_TLS *xio;
132
133 assert(sio);
134 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
135 assert(xio);
136
137 DBG_WARN(GWEN_LOGDOMAIN, "No checkCertFn set, using GWEN_GUI");
138 return GWEN_Gui_CheckCert(cert, sio, 0);
139 }
140
141
142
GWEN_SyncIo_Tls_CheckCert(GWEN_SYNCIO * sio,const GWEN_SSLCERTDESCR * cert)143 int GWEN_SyncIo_Tls_CheckCert(GWEN_SYNCIO *sio, const GWEN_SSLCERTDESCR *cert)
144 {
145 GWEN_SYNCIO_TLS *xio;
146
147 assert(sio);
148 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
149 assert(xio);
150
151 if (xio->checkCertFn) {
152 /* try my own checkCert function first */
153 return xio->checkCertFn(sio, cert);
154 }
155 else {
156 /* none set, call the check cert function of GWEN_GUI (for older code) */
157 DBG_ERROR(GWEN_LOGDOMAIN, "No checkCertFn set, falling back to GUI (SNH!)");
158 return GWEN_SyncIo_Tls_Internal_CheckCert(sio, cert);
159 }
160 }
161
162
163
GWEN_SyncIo_Tls_GetLocalCertFile(const GWEN_SYNCIO * sio)164 const char *GWEN_SyncIo_Tls_GetLocalCertFile(const GWEN_SYNCIO *sio)
165 {
166 GWEN_SYNCIO_TLS *xio;
167
168 assert(sio);
169 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
170 assert(xio);
171
172 return xio->localCertFile;
173 }
174
175
176
GWEN_SyncIo_Tls_SetLocalCertFile(GWEN_SYNCIO * sio,const char * s)177 void GWEN_SyncIo_Tls_SetLocalCertFile(GWEN_SYNCIO *sio, const char *s)
178 {
179 GWEN_SYNCIO_TLS *xio;
180
181 assert(sio);
182 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
183 assert(xio);
184
185 free(xio->localCertFile);
186 if (s)
187 xio->localCertFile=strdup(s);
188 else
189 xio->localCertFile=NULL;
190 }
191
192
193
GWEN_SyncIo_Tls_GetLocalKeyFile(const GWEN_SYNCIO * sio)194 const char *GWEN_SyncIo_Tls_GetLocalKeyFile(const GWEN_SYNCIO *sio)
195 {
196 GWEN_SYNCIO_TLS *xio;
197
198 assert(sio);
199 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
200 assert(xio);
201
202 return xio->localKeyFile;
203 }
204
205
206
GWEN_SyncIo_Tls_SetLocalKeyFile(GWEN_SYNCIO * sio,const char * s)207 void GWEN_SyncIo_Tls_SetLocalKeyFile(GWEN_SYNCIO *sio, const char *s)
208 {
209 GWEN_SYNCIO_TLS *xio;
210
211 assert(sio);
212 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
213 assert(xio);
214
215 free(xio->localKeyFile);
216 if (s)
217 xio->localKeyFile=strdup(s);
218 else
219 xio->localKeyFile=NULL;
220 }
221
222
223
GWEN_SyncIo_Tls_GetLocalTrustFile(const GWEN_SYNCIO * sio)224 const char *GWEN_SyncIo_Tls_GetLocalTrustFile(const GWEN_SYNCIO *sio)
225 {
226 GWEN_SYNCIO_TLS *xio;
227
228 assert(sio);
229 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
230 assert(xio);
231
232 return xio->localTrustFile;
233 }
234
235
236
GWEN_SyncIo_Tls_SetLocalTrustFile(GWEN_SYNCIO * sio,const char * s)237 void GWEN_SyncIo_Tls_SetLocalTrustFile(GWEN_SYNCIO *sio, const char *s)
238 {
239 GWEN_SYNCIO_TLS *xio;
240
241 assert(sio);
242 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
243 assert(xio);
244
245 free(xio->localTrustFile);
246 if (s)
247 xio->localTrustFile=strdup(s);
248 else
249 xio->localTrustFile=NULL;
250 }
251
252
253
GWEN_SyncIo_Tls_GetDhParamFile(const GWEN_SYNCIO * sio)254 const char *GWEN_SyncIo_Tls_GetDhParamFile(const GWEN_SYNCIO *sio)
255 {
256 GWEN_SYNCIO_TLS *xio;
257
258 assert(sio);
259 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
260 assert(xio);
261
262 return xio->dhParamFile;
263 }
264
265
266
GWEN_SyncIo_Tls_SetDhParamFile(GWEN_SYNCIO * sio,const char * s)267 void GWEN_SyncIo_Tls_SetDhParamFile(GWEN_SYNCIO *sio, const char *s)
268 {
269 GWEN_SYNCIO_TLS *xio;
270
271 assert(sio);
272 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
273 assert(xio);
274
275 free(xio->dhParamFile);
276 if (s)
277 xio->dhParamFile=strdup(s);
278 else
279 xio->dhParamFile=NULL;
280 }
281
282
283
GWEN_SyncIo_Tls_GetRemoteHostName(const GWEN_SYNCIO * sio)284 const char *GWEN_SyncIo_Tls_GetRemoteHostName(const GWEN_SYNCIO *sio)
285 {
286 GWEN_SYNCIO_TLS *xio;
287
288 assert(sio);
289 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
290 assert(xio);
291
292 return xio->hostName;
293 }
294
295
296
GWEN_SyncIo_Tls_SetRemoteHostName(GWEN_SYNCIO * sio,const char * s)297 void GWEN_SyncIo_Tls_SetRemoteHostName(GWEN_SYNCIO *sio, const char *s)
298 {
299 GWEN_SYNCIO_TLS *xio;
300
301 assert(sio);
302 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
303 assert(xio);
304
305 free(xio->hostName);
306 if (s)
307 xio->hostName=strdup(s);
308 else
309 xio->hostName=NULL;
310 }
311
312
313
GWEN_SyncIo_Tls_GetPeerCertDescr(const GWEN_SYNCIO * sio)314 GWEN_SSLCERTDESCR *GWEN_SyncIo_Tls_GetPeerCertDescr(const GWEN_SYNCIO *sio)
315 {
316 GWEN_SYNCIO_TLS *xio;
317
318 assert(sio);
319 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
320 assert(xio);
321
322 return xio->peerCertDescr;
323 }
324
325
326
GWEN_SyncIo_Tls__readFile(const char * fname,GWEN_BUFFER * buf)327 int GWEN_SyncIo_Tls__readFile(const char *fname, GWEN_BUFFER *buf)
328 {
329 FILE *f;
330
331 f=fopen(fname, "r");
332 if (f==NULL)
333 return GWEN_ERROR_IO;
334
335 while (!feof(f)) {
336 int rv;
337
338 GWEN_Buffer_AllocRoom(buf, 512);
339 rv=fread(GWEN_Buffer_GetPosPointer(buf), 1, 512, f);
340 if (rv==0)
341 break;
342 else if (rv<0) {
343 DBG_INFO(GWEN_LOGDOMAIN, "fread(%s): %s", fname, strerror(errno));
344 fclose(f);
345 return GWEN_ERROR_IO;
346 }
347 else {
348 GWEN_Buffer_IncrementPos(buf, rv);
349 GWEN_Buffer_AdjustUsedBytes(buf);
350 }
351 }
352 fclose(f);
353 return 0;
354 }
355
356
357
358
359 #if GWEN_TLS_USE_SYSTEM_CERTIFICATES
360 # ifndef OS_WIN32
GWEN_SyncIo_Tls_AddCaCertFolder(GWEN_SYNCIO * sio,const char * folder)361 static int GWEN_SyncIo_Tls_AddCaCertFolder(GWEN_SYNCIO *sio, const char *folder)
362 {
363 GWEN_SYNCIO_TLS *xio;
364 int rv;
365 int successfullTustFileCount=0;
366
367 assert(sio);
368 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
369 assert(xio);
370
371 if (folder && *folder) {
372 GWEN_STRINGLIST *fileList;
373
374 fileList=GWEN_StringList_new();
375 rv=GWEN_Directory_GetMatchingFilesRecursively(folder, fileList, "*.crt");
376 if (rv<0) {
377 DBG_ERROR(GWEN_LOGDOMAIN,
378 "Error reading list of certificate files (%d) in folder [%s]",
379 rv, folder);
380 }
381 else {
382 GWEN_STRINGLISTENTRY *se;
383
384 se=GWEN_StringList_FirstEntry(fileList);
385 while (se) {
386 const char *s;
387
388 s=GWEN_StringListEntry_Data(se);
389 if (s && *s) {
390 rv=gnutls_certificate_set_x509_trust_file(xio->credentials,
391 s,
392 GNUTLS_X509_FMT_PEM);
393 if (rv<=0) {
394 DBG_WARN(GWEN_LOGDOMAIN,
395 "gnutls_certificate_set_x509_trust_file(%s): %d (%s)",
396 s, rv, gnutls_strerror(rv));
397 }
398 else {
399 DBG_INFO(GWEN_LOGDOMAIN, "Added %d trusted certs from [%s]", rv, s);
400 successfullTustFileCount++;
401 }
402 }
403
404 se=GWEN_StringListEntry_Next(se);
405 } /* while */
406 }
407 GWEN_StringList_free(fileList);
408 }
409
410 if (successfullTustFileCount==0) {
411 DBG_ERROR(GWEN_LOGDOMAIN, "No files added from folder [%s]", folder);
412 }
413
414 return successfullTustFileCount;
415 }
416 # endif
417 #endif
418
419
420
GWEN_SyncIo_Tls_Prepare(GWEN_SYNCIO * sio)421 int GWEN_SyncIo_Tls_Prepare(GWEN_SYNCIO *sio)
422 {
423 GWEN_SYNCIO_TLS *xio;
424 int rv;
425 uint32_t lflags;
426 const char *custom_ciphers;
427 const char *errPos=NULL;
428
429 assert(sio);
430 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
431 assert(xio);
432
433 lflags=GWEN_SyncIo_GetFlags(sio);
434 DBG_INFO(GWEN_LOGDOMAIN, "Preparing SSL (%08x)", lflags);
435
436 /* init session */
437 if (lflags & GWEN_SYNCIO_FLAGS_PASSIVE) {
438 DBG_INFO(GWEN_LOGDOMAIN, "Init as server");
439 rv=gnutls_init(&xio->session, GNUTLS_SERVER);
440 }
441 else {
442 DBG_INFO(GWEN_LOGDOMAIN, "Init as client");
443 rv=gnutls_init(&xio->session, GNUTLS_CLIENT);
444 }
445 if (rv) {
446 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_init: %d (%s)", rv, gnutls_strerror(rv));
447 return GWEN_ERROR_GENERIC;
448 }
449
450 /* set cipher priorities */
451 custom_ciphers=getenv("GWEN_TLS_CIPHER_PRIORITIES");
452 /* TODO: make custom ciphers configurable as priority string? */
453 if (custom_ciphers && *custom_ciphers) { /* use cipher list from env var */
454 GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Info, I18N("TLS: SSL cipher priority list: %s"), custom_ciphers);
455 rv=gnutls_priority_set_direct(xio->session, custom_ciphers, &errPos);
456 if (rv!=GNUTLS_E_SUCCESS) {
457 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_priority_set_direct using '%s' failed: %s (%d) [%s]",
458 custom_ciphers, gnutls_strerror(rv), rv, errPos?errPos:"");
459 gnutls_deinit(xio->session);
460 return GWEN_ERROR_GENERIC;
461 }
462 }
463 else { /* use default ciphers from GnuTLS */
464 GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Notice, I18N("Using GnuTLS default ciphers."));
465 rv=gnutls_set_default_priority(xio->session);
466 if (rv!=GNUTLS_E_SUCCESS) {
467 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_set_default_priority failed: %s (%d)", gnutls_strerror(rv), rv);
468 gnutls_deinit(xio->session);
469 return GWEN_ERROR_GENERIC;
470 }
471 }
472
473 /* protect against too-many-known-ca problem */
474 gnutls_handshake_set_max_packet_length(xio->session, 64*1024);
475
476 /* let a server request peer certs */
477 if ((lflags & GWEN_SYNCIO_FLAGS_PASSIVE) &&
478 (lflags & GWEN_SYNCIO_TLS_FLAGS_REQUEST_CERT))
479 gnutls_certificate_server_set_request(xio->session, GNUTLS_CERT_REQUIRE);
480
481 /* prepare cert credentials */
482 rv=gnutls_certificate_allocate_credentials(&xio->credentials);
483 if (rv) {
484 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_certificate_allocate_credentials: %d (%s)", rv, gnutls_strerror(rv));
485 gnutls_deinit(xio->session);
486 return GWEN_ERROR_GENERIC;
487 }
488
489 /* possibly set key file and cert file */
490 if (xio->localCertFile && xio->localKeyFile) {
491 rv=gnutls_certificate_set_x509_key_file(xio->credentials,
492 xio->localCertFile,
493 xio->localKeyFile,
494 GNUTLS_X509_FMT_PEM);
495 if (rv<0) {
496 if (rv) {
497 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_certificate_set_x509_key_file: %d (%s)", rv, gnutls_strerror(rv));
498 gnutls_certificate_free_credentials(xio->credentials);
499 gnutls_deinit(xio->session);
500 return GWEN_ERROR_GENERIC;
501 }
502 }
503 }
504
505 /* find default trust file if none is selected */
506 if (lflags & GWEN_SYNCIO_TLS_FLAGS_ADD_TRUSTED_CAS) {
507 #if GWEN_TLS_USE_SYSTEM_CERTIFICATES
508 /* disable setting of default trust file as discussed on aqbanking-users.
509 * The rationale is that without this file being set gnutls should behave
510 * correctly on each system.
511 * On Linux systems it should use the standard mechanism of the underlying
512 * distribution. On Windows the default CA store should be used (if given
513 * "--with-default-trust-store-file" to "./configure" of GNUTLS).
514 */
515 int trustFileSet=0;
516
517
518 if (trustFileSet==0) {
519 /* Adds the system's default trusted CAs in order to verify client or server certificates. */
520 rv=gnutls_certificate_set_x509_system_trust(xio->credentials);
521 if (rv<=0) {
522 DBG_WARN(GWEN_LOGDOMAIN, "gnutls_certificate_set_x509_system_trust: %d (%s)", rv, gnutls_strerror(rv));
523 }
524 else {
525 DBG_INFO(GWEN_LOGDOMAIN, "Added %d default trusted certs from system", rv);
526 trustFileSet=1;
527 }
528 }
529
530 /* try to find OpenSSL certificates */
531 # ifdef OS_WIN32
532 if (trustFileSet==0) {
533 char defaultPath[2*MAX_PATH+1];
534 const char *defaultFile = "ca-bundle.crt";
535 GWEN_STRINGLIST *paths;
536 GWEN_BUFFER *nbuf;
537
538 if (GWEN_Directory_GetPrefixDirectory(defaultPath, sizeof(defaultPath))) {
539 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_certificate_set_x509_key_file: could not get install prefix");
540 return GWEN_ERROR_GENERIC;
541 }
542 if (strcat_s(defaultPath, sizeof(defaultPath), "\\share\\gwenhywfar")) {
543 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_certificate_set_x509_key_file: no memory on creating search path");
544 return GWEN_ERROR_GENERIC;
545 }
546
547 paths=GWEN_StringList_new();
548 GWEN_StringList_AppendString(paths, defaultPath, 0, 0);
549
550 nbuf=GWEN_Buffer_new(0, 256, 0, 1);
551 rv=GWEN_Directory_FindFileInPaths(paths, defaultFile, nbuf);
552 GWEN_StringList_free(paths);
553 if (rv==0) {
554 DBG_INFO(GWEN_LOGDOMAIN,
555 "Using default ca-bundle from [%s]",
556 GWEN_Buffer_GetStart(nbuf));
557
558 rv=gnutls_certificate_set_x509_trust_file(xio->credentials,
559 GWEN_Buffer_GetStart(nbuf),
560 GNUTLS_X509_FMT_PEM);
561 if (rv<=0) {
562 DBG_WARN(GWEN_LOGDOMAIN,
563 "gnutls_certificate_set_x509_trust_file(%s): %d (%s)",
564 GWEN_Buffer_GetStart(nbuf), rv, gnutls_strerror(rv));
565 }
566 else {
567 DBG_INFO(GWEN_LOGDOMAIN,
568 "Added %d trusted certs from [%s]", rv, GWEN_Buffer_GetStart(nbuf));
569 trustFileSet=1;
570 }
571 }
572 GWEN_Buffer_free(nbuf);
573 }
574 # endif
575
576
577 # ifndef OS_WIN32
578 /* try to finde certificate bundle */
579 if (trustFileSet==0) {
580 int i;
581 const char *sCertFile=NULL;
582
583 for (i=0; ; i++) {
584 sCertFile=SYNCIO_TLS_SYSTEM_CERTFILES[i];
585 if (sCertFile==NULL)
586 break;
587 if (0==GWEN_Directory_GetPath(sCertFile, GWEN_PATH_FLAGS_NAMEMUSTEXIST | GWEN_PATH_FLAGS_VARIABLE)) {
588 DBG_INFO(GWEN_LOGDOMAIN, "Found system-wide cert bundle in %s", sCertFile);
589 break;
590 }
591 }
592
593 if (sCertFile && *sCertFile) {
594 rv=gnutls_certificate_set_x509_trust_file(xio->credentials, sCertFile, GNUTLS_X509_FMT_PEM);
595 if (rv<=0) {
596 DBG_WARN(GWEN_LOGDOMAIN, "gnutls_certificate_set_x509_trust_file(%s): %d (%s)", sCertFile, rv, gnutls_strerror(rv));
597 }
598 else {
599 DBG_INFO(GWEN_LOGDOMAIN, "Added %d trusted certs from [%s]", rv, sCertFile);
600 trustFileSet=1;
601 }
602 }
603 else {
604 DBG_ERROR(GWEN_LOGDOMAIN, "No system-wide certificate bundle found.");
605 }
606 }
607
608 /* try to find ca-certificates (at least available on Debian systems) */
609 if (trustFileSet==0) {
610 rv=GWEN_Directory_GetPath("/usr/share/ca-certificates", GWEN_PATH_FLAGS_NAMEMUSTEXIST);
611 if (rv>=0) {
612 rv=GWEN_SyncIo_Tls_AddCaCertFolder(sio, "/usr/share/ca-certificates");
613 if (rv<=0) {
614 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
615 }
616 else {
617 trustFileSet=1;
618 }
619 }
620 }
621
622 # endif
623
624
625 if (trustFileSet==0) {
626
627 /* TODO: use gnutls_certificate_set_x509_system_trust() */
628 trustFileSet=1;
629 }
630
631
632
633 if (trustFileSet==0) {
634 DBG_WARN(GWEN_LOGDOMAIN, "No default bundle file found");
635 }
636 #endif
637 }
638
639 /* possibly set trust file */
640 if (xio->localTrustFile) {
641 rv=gnutls_certificate_set_x509_trust_file(xio->credentials,
642 xio->localTrustFile,
643 GNUTLS_X509_FMT_PEM);
644 if (rv<=0) {
645 DBG_ERROR(GWEN_LOGDOMAIN,
646 "gnutls_certificate_set_x509_trust_file(%s): %d (%s)",
647 (xio->localTrustFile)?(xio->localTrustFile):"-none-",
648 rv, gnutls_strerror(rv));
649 gnutls_certificate_free_credentials(xio->credentials);
650 gnutls_deinit(xio->session);
651 return GWEN_ERROR_GENERIC;
652 }
653 else {
654 DBG_INFO(GWEN_LOGDOMAIN,
655 "Added %d trusted certs", rv);
656 }
657 }
658
659 /* possibly set DH params */
660 if (xio->dhParamFile) {
661 GWEN_BUFFER *dbuf;
662
663 dbuf=GWEN_Buffer_new(0, 256, 0, 1);
664 rv=GWEN_SyncIo_Tls__readFile(xio->dhParamFile, dbuf);
665 if (rv) {
666 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
667 GWEN_Buffer_free(dbuf);
668 gnutls_certificate_free_credentials(xio->credentials);
669 gnutls_deinit(xio->session);
670 return rv;
671 }
672 else {
673 gnutls_datum_t d;
674 gnutls_dh_params_t dh_params=NULL;
675
676 rv=gnutls_dh_params_init(&dh_params);
677 if (rv<0) {
678 GWEN_Buffer_free(dbuf);
679 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_dh_params_init: %d (%s)", rv, gnutls_strerror(rv));
680 gnutls_certificate_free_credentials(xio->credentials);
681 gnutls_deinit(xio->session);
682 return GWEN_ERROR_GENERIC;
683 }
684
685 d.size=GWEN_Buffer_GetUsedBytes(dbuf);
686 d.data=(unsigned char *)GWEN_Buffer_GetStart(dbuf);
687
688 rv=gnutls_dh_params_import_pkcs3(dh_params, &d, GNUTLS_X509_FMT_PEM);
689 if (rv<0) {
690 GWEN_Buffer_free(dbuf);
691 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_dh_params_import_pkcs3: %d (%s)", rv, gnutls_strerror(rv));
692 gnutls_certificate_free_credentials(xio->credentials);
693 gnutls_deinit(xio->session);
694 return GWEN_ERROR_GENERIC;
695 }
696 GWEN_Buffer_free(dbuf);
697
698 gnutls_certificate_set_dh_params(xio->credentials, dh_params);
699 }
700 }
701
702 /* set credentials in TLS session */
703 rv=gnutls_credentials_set(xio->session, GNUTLS_CRD_CERTIFICATE, xio->credentials);
704 if (rv<0) {
705 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_credentials_set: %d (%s)", rv, gnutls_strerror(rv));
706 gnutls_certificate_free_credentials(xio->credentials);
707 gnutls_deinit(xio->session);
708 return GWEN_ERROR_GENERIC;
709 }
710
711 /* if hostname set try to set it */
712 if (xio->hostName) {
713 rv=gnutls_server_name_set(xio->session, GNUTLS_NAME_DNS, xio->hostName, strlen(xio->hostName));
714 if (rv!=GNUTLS_E_SUCCESS) {
715 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_server_name_set: %d (%s), ignoring", rv, gnutls_strerror(rv));
716 }
717 }
718
719 /* we use our own push/pull functions */
720 gnutls_transport_set_ptr(xio->session, (gnutls_transport_ptr_t)sio);
721 gnutls_transport_set_push_function(xio->session, GWEN_SyncIo_Tls_Push);
722 gnutls_transport_set_pull_function(xio->session, GWEN_SyncIo_Tls_Pull);
723 #if GNUTLS_VERSION_NUMBER < 0x020c00
724 /* This function must be set to 0 in GNUTLS versions < 2.12.0 because we use
725 * custom push/pull functions.
726 * In GNUTLS 2.12.x this is set to 0 and since version 3 this functions is removed
727 * completely.
728 * So we only call this function now for GNUTLS < 2.12.0.
729 */
730 gnutls_transport_set_lowat(xio->session, 0);
731 #endif
732
733 xio->prepared=1;
734
735 return 0;
736 }
737
738
739
GWEN_SyncIo_Tls_UndoPrepare(GWEN_SYNCIO * sio)740 void GWEN_SyncIo_Tls_UndoPrepare(GWEN_SYNCIO *sio)
741 {
742 GWEN_SYNCIO_TLS *xio;
743
744 assert(sio);
745 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
746 assert(xio);
747
748 if (xio->prepared) {
749 gnutls_certificate_free_credentials(xio->credentials);
750 gnutls_deinit(xio->session);
751 xio->prepared=0;
752 }
753 }
754
755
756
GWEN_SyncIo_Tls_GetPeerCert(GWEN_SYNCIO * sio)757 int GWEN_SyncIo_Tls_GetPeerCert(GWEN_SYNCIO *sio)
758 {
759 GWEN_SYNCIO_TLS *xio;
760 const gnutls_datum_t *cert_list;
761 unsigned int cert_list_size;
762 size_t size;
763 GWEN_SSLCERTDESCR *certDescr;
764 char buffer1[64];
765 time_t t0;
766 int rv;
767 uint32_t lflags;
768 uint32_t errFlags=0;
769 int i;
770 unsigned int status;
771 GWEN_BUFFER *sbuf=NULL;
772
773 assert(sio);
774 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
775 assert(xio);
776
777 lflags=GWEN_SyncIo_GetFlags(sio);
778
779 if (xio->peerCertDescr) {
780 GWEN_SslCertDescr_free(xio->peerCertDescr);
781 xio->peerCertDescr=NULL;
782 }
783 xio->peerCertFlags=0;
784
785 t0=time(NULL);
786 if (t0<0) {
787 DBG_WARN(GWEN_LOGDOMAIN, "Could not get system time");
788 errFlags|=GWEN_SSL_CERT_FLAGS_SYSTEM;
789 }
790
791 /* create new cert description, check cert on the fly */
792 certDescr=GWEN_SslCertDescr_new();
793
794 /* some general tests */
795 if (lflags & GWEN_SYNCIO_TLS_FLAGS_ALLOW_V1_CA_CRT)
796 gnutls_certificate_set_verify_flags(xio->credentials,
797 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
798
799 rv=gnutls_certificate_verify_peers2(xio->session, &status);
800 if (rv<0) {
801 DBG_INFO(GWEN_LOGDOMAIN, "gnutls_certificate_verify_peers2: %d (%s)", rv, gnutls_strerror(rv));
802 GWEN_SslCertDescr_free(certDescr);
803 return GWEN_ERROR_SSL_SECURITY;
804 }
805
806 if (gnutls_certificate_type_get(xio->session)!=GNUTLS_CRT_X509) {
807 DBG_INFO(GWEN_LOGDOMAIN, "Certificate is not X.509");
808
809 GWEN_SslCertDescr_free(certDescr);
810 return GWEN_ERROR_SSL_SECURITY;
811 }
812
813 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
814 DBG_INFO(GWEN_LOGDOMAIN, "Signer not found");
815 GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Warning,
816 I18N("Signer not found"));
817 errFlags|=GWEN_SSL_CERT_FLAGS_SIGNER_NOT_FOUND;
818 }
819
820 if (status & GNUTLS_CERT_INVALID) {
821 DBG_INFO(GWEN_LOGDOMAIN, "Certificate is not trusted");
822 GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Warning,
823 I18N("Certificate is not trusted"));
824 errFlags|=GWEN_SSL_CERT_FLAGS_INVALID;
825 }
826
827 if (status & GNUTLS_CERT_REVOKED) {
828 DBG_INFO(GWEN_LOGDOMAIN, "Certificate has been revoked");
829 GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Warning,
830 I18N("Certificate has been revoked"));
831 errFlags|=GWEN_SSL_CERT_FLAGS_REVOKED;
832 }
833
834 cert_list=gnutls_certificate_get_peers(xio->session, &cert_list_size);
835 if (cert_list==NULL || cert_list_size==0) {
836 DBG_INFO(GWEN_LOGDOMAIN, "No peer certificates found");
837 return GWEN_ERROR_NO_DATA;
838 }
839
840 for (i=0; i<(int) cert_list_size; i++) {
841 gnutls_x509_crt_t cert;
842 time_t t;
843
844 rv=gnutls_x509_crt_init(&cert);
845 if (rv!=0) {
846 DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_init: %d (%s)", rv, gnutls_strerror(rv));
847 return GWEN_ERROR_GENERIC;
848 }
849
850 rv=gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); /* TODO: shouldn't we use the index?? */
851 if (rv!=0) {
852 DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_import: %d (%s)", rv, gnutls_strerror(rv));
853 gnutls_x509_crt_deinit(cert);
854 return GWEN_ERROR_GENERIC;
855 }
856
857 if (i==0) {
858 gnutls_datum_t n= {NULL, 0};
859 gnutls_datum_t e= {NULL, 0};
860
861 /* get public key from cert, if any */
862 rv=gnutls_x509_crt_get_pk_rsa_raw(cert, &n, &e);
863 if (rv!=0) {
864 DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_get_pk_rsa_raw: %d (%s)", rv, gnutls_strerror(rv));
865 }
866 else {
867 GWEN_BUFFER *kbuf;
868
869 DBG_INFO(GWEN_LOGDOMAIN, "Key stored within certificate, extracting (modlen=%d, explen=%d)",
870 n.size, e.size);
871
872 kbuf=GWEN_Buffer_new(0, 256, 0, 1);
873
874 if (n.data && n.size) {
875 /* store public modulus */
876 GWEN_Text_ToHexBuffer((const char *)(n.data), n.size, kbuf, 0, 0, 0);
877 GWEN_SslCertDescr_SetPubKeyModulus(certDescr, GWEN_Buffer_GetStart(kbuf));
878 GWEN_Buffer_Reset(kbuf);
879 }
880
881 if (e.data && e.size) {
882 /* store public exponent */
883 GWEN_Text_ToHexBuffer((const char *)(e.data), e.size, kbuf, 0, 0, 0);
884 GWEN_SslCertDescr_SetPubKeyExponent(certDescr, GWEN_Buffer_GetStart(kbuf));
885 GWEN_Buffer_Reset(kbuf);
886 }
887
888 GWEN_Buffer_free(kbuf);
889 if (n.data)
890 gcry_free(n.data);
891 if (e.data)
892 gcry_free(e.data);
893 }
894
895 /* get fingerprint (MD5) */
896 size=16;
897 rv=gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_MD5, buffer1, &size);
898 if (rv!=0) {
899 DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_get_fingerprint(MD5): %d (%s)", rv, gnutls_strerror(rv));
900 GWEN_SslCertDescr_free(certDescr);
901 gnutls_x509_crt_deinit(cert);
902 return GWEN_ERROR_GENERIC;
903 }
904 else {
905 GWEN_BUFFER *dbuf;
906
907 dbuf=GWEN_Buffer_new(0, 256, 0, 1);
908 if (GWEN_Text_ToHexBuffer(/* GCC4 pointer-signedness fix: */ buffer1,
909 size, dbuf, 2, ':', 0)) {
910 DBG_ERROR(GWEN_LOGDOMAIN,
911 "Could not convert fingerprint to hex");
912 }
913 else {
914 GWEN_SslCertDescr_SetFingerPrint(certDescr, GWEN_Buffer_GetStart(dbuf));
915 }
916 GWEN_Buffer_free(dbuf);
917 }
918
919 /* get fingerprint (SHA1) */
920 size=sizeof(buffer1);
921 rv=gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, buffer1, &size);
922 if (rv!=0) {
923 DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_get_fingerprint(SHA1): %d (%s)", rv, gnutls_strerror(rv));
924 GWEN_SslCertDescr_free(certDescr);
925 gnutls_x509_crt_deinit(cert);
926 return GWEN_ERROR_GENERIC;
927 }
928 else {
929 GWEN_BUFFER *dbuf;
930
931 dbuf=GWEN_Buffer_new(0, 256, 0, 1);
932 if (GWEN_Text_ToHexBuffer(/* GCC4 pointer-signedness fix: */ buffer1,
933 size, dbuf, 2, ':', 0)) {
934 DBG_ERROR(GWEN_LOGDOMAIN,
935 "Could not convert fingerprint to hex");
936 }
937 else {
938 GWEN_SslCertDescr_SetFingerPrintSha1(certDescr, GWEN_Buffer_GetStart(dbuf));
939 }
940 GWEN_Buffer_free(dbuf);
941 }
942
943 /* get fingerprint (SHA512) */
944 size=sizeof(buffer1);
945 rv=gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA512, buffer1, &size);
946 if (rv!=0) {
947 DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_get_fingerprint(SHA512): %d (%s)", rv, gnutls_strerror(rv));
948 GWEN_SslCertDescr_free(certDescr);
949 gnutls_x509_crt_deinit(cert);
950 return GWEN_ERROR_GENERIC;
951 }
952 else {
953 GWEN_BUFFER *dbuf;
954
955 dbuf=GWEN_Buffer_new(0, 256, 0, 1);
956 if (GWEN_Text_ToHexBuffer(/* GCC4 pointer-signedness fix: */ buffer1,
957 size, dbuf, 2, ':', 0)) {
958 DBG_ERROR(GWEN_LOGDOMAIN,
959 "Could not convert fingerprint to hex");
960 }
961 else {
962 GWEN_SslCertDescr_SetFingerPrintSha512(certDescr, GWEN_Buffer_GetStart(dbuf));
963 }
964 GWEN_Buffer_free(dbuf);
965 }
966
967
968 if (xio->hostName) {
969 DBG_INFO(GWEN_LOGDOMAIN, "Checking hostname [%s]", xio->hostName);
970 if (!gnutls_x509_crt_check_hostname(cert, xio->hostName)) {
971 DBG_WARN(GWEN_LOGDOMAIN,
972 "Certificate was not issued for this host");
973 GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Warning,
974 I18N("Certificate was not issued for this host"));
975 errFlags|=GWEN_SSL_CERT_FLAGS_BAD_HOSTNAME;
976 }
977 else {
978 DBG_INFO(GWEN_LOGDOMAIN, "Cert is for this server");
979 }
980 }
981 else {
982 DBG_WARN(GWEN_LOGDOMAIN,
983 "Hostname is not set, unable to verify the sender");
984 GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Warning,
985 I18N("No hostname to verify the sender!"));
986 }
987
988 }
989
990 /* get activation time */
991 t=gnutls_x509_crt_get_activation_time(cert);
992 if (t<0) {
993 DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_get_activation_time: %d (%s)", rv, gnutls_strerror(rv));
994 errFlags|=GWEN_SSL_CERT_FLAGS_BAD_DATA;
995 }
996 else {
997 if (t>t0) {
998 DBG_INFO(GWEN_LOGDOMAIN, "Cert is not yet active");
999 errFlags|=GWEN_SSL_CERT_FLAGS_NOT_ACTIVE;
1000 }
1001 if (i==0) {
1002 GWEN_TIME *ti;
1003
1004 ti=GWEN_Time_fromSeconds(t);
1005 if (ti)
1006 GWEN_SslCertDescr_SetNotBefore(certDescr, ti);
1007 GWEN_Time_free(ti);
1008 }
1009 }
1010
1011 /* get expiration time */
1012 t=gnutls_x509_crt_get_expiration_time(cert);
1013 if (t<0) {
1014 DBG_INFO(GWEN_LOGDOMAIN, "gnutls_x509_crt_get_expiration_time: %d (%s)", rv, gnutls_strerror(rv));
1015 errFlags|=GWEN_SSL_CERT_FLAGS_BAD_DATA;
1016 }
1017 else {
1018 if (t<t0) {
1019 DBG_INFO(GWEN_LOGDOMAIN, "Cert has expired");
1020 errFlags|=GWEN_SSL_CERT_FLAGS_EXPIRED;
1021 }
1022 if (i==0) {
1023 GWEN_TIME *ti;
1024
1025 ti=GWEN_Time_fromSeconds(t);
1026 if (ti)
1027 GWEN_SslCertDescr_SetNotAfter(certDescr, ti);
1028 GWEN_Time_free(ti);
1029 }
1030 }
1031
1032 if (i==0) {
1033 /* get owner information, but only for first cert */
1034 size=sizeof(buffer1)-1;
1035 rv=gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, buffer1, &size);
1036 if (rv==0) {
1037 GWEN_SslCertDescr_SetCommonName(certDescr, buffer1);
1038 if (xio->hostName && strcasecmp(xio->hostName, buffer1)!=0) {
1039 DBG_INFO(GWEN_LOGDOMAIN, "Owner of certificate does not match hostname");
1040 errFlags|=GWEN_SSL_CERT_FLAGS_BAD_HOSTNAME;
1041 }
1042 }
1043
1044 size=sizeof(buffer1)-1;
1045 rv=gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, buffer1, &size);
1046 if (rv==0)
1047 GWEN_SslCertDescr_SetOrganizationName(certDescr, buffer1);
1048
1049 size=sizeof(buffer1)-1;
1050 rv=gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, buffer1, &size);
1051 if (rv==0)
1052 GWEN_SslCertDescr_SetOrganizationalUnitName(certDescr, buffer1);
1053
1054 size=sizeof(buffer1)-1;
1055 rv=gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, buffer1, &size);
1056 if (rv==0)
1057 GWEN_SslCertDescr_SetLocalityName(certDescr, buffer1);
1058
1059 size=sizeof(buffer1)-1;
1060 rv=gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, buffer1, &size);
1061 if (rv==0)
1062 GWEN_SslCertDescr_SetStateOrProvinceName(certDescr, buffer1);
1063
1064 size=sizeof(buffer1)-1;
1065 rv=gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, buffer1, &size);
1066 if (rv==0)
1067 GWEN_SslCertDescr_SetCountryName(certDescr, buffer1);
1068 }
1069
1070 gnutls_x509_crt_deinit(cert);
1071 }
1072
1073 /* done */
1074 if (errFlags)
1075 GWEN_SslCertDescr_SetIsError(certDescr, 1);
1076 else
1077 errFlags|=GWEN_SSL_CERT_FLAGS_OK;
1078
1079 sbuf=GWEN_Buffer_new(0, 256, 0, 1);
1080
1081 if (errFlags & GWEN_SSL_CERT_FLAGS_SIGNER_NOT_FOUND) {
1082 if (GWEN_Buffer_GetUsedBytes(sbuf))
1083 GWEN_Buffer_AppendString(sbuf, "; ");
1084 GWEN_Buffer_AppendString(sbuf, I18N("Signer not found"));
1085 }
1086
1087 if (errFlags & GWEN_SSL_CERT_FLAGS_INVALID) {
1088 if (GWEN_Buffer_GetUsedBytes(sbuf))
1089 GWEN_Buffer_AppendString(sbuf, "; ");
1090 GWEN_Buffer_AppendString(sbuf, I18N("Certificate is not trusted"));
1091 }
1092
1093 if (errFlags & GWEN_SSL_CERT_FLAGS_REVOKED) {
1094 if (GWEN_Buffer_GetUsedBytes(sbuf))
1095 GWEN_Buffer_AppendString(sbuf, "; ");
1096 GWEN_Buffer_AppendString(sbuf, I18N("Certificate has been revoked"));
1097 }
1098
1099 if (errFlags & GWEN_SSL_CERT_FLAGS_EXPIRED) {
1100 if (GWEN_Buffer_GetUsedBytes(sbuf))
1101 GWEN_Buffer_AppendString(sbuf, "; ");
1102 GWEN_Buffer_AppendString(sbuf, I18N("Certificate has expired"));
1103 }
1104
1105 if (errFlags & GWEN_SSL_CERT_FLAGS_NOT_ACTIVE) {
1106 if (GWEN_Buffer_GetUsedBytes(sbuf))
1107 GWEN_Buffer_AppendString(sbuf, "; ");
1108 GWEN_Buffer_AppendString(sbuf, I18N("Certificate is not active yet"));
1109 }
1110
1111 if (errFlags & GWEN_SSL_CERT_FLAGS_BAD_HOSTNAME) {
1112 if (GWEN_Buffer_GetUsedBytes(sbuf))
1113 GWEN_Buffer_AppendString(sbuf, "; ");
1114 GWEN_Buffer_AppendString(sbuf, I18N("Certificate owner does not match hostname"));
1115 }
1116
1117 if (errFlags & GWEN_SSL_CERT_FLAGS_BAD_DATA) {
1118 if (GWEN_Buffer_GetUsedBytes(sbuf))
1119 GWEN_Buffer_AppendString(sbuf, "; ");
1120 GWEN_Buffer_AppendString(sbuf, I18N("Certificate contains invalid information"));
1121 }
1122
1123 if (errFlags & GWEN_SSL_CERT_FLAGS_SYSTEM) {
1124 if (GWEN_Buffer_GetUsedBytes(sbuf))
1125 GWEN_Buffer_AppendString(sbuf, "; ");
1126 GWEN_Buffer_AppendString(sbuf, I18N("A system error occurred while checking the certificate"));
1127 }
1128
1129 if (errFlags & GWEN_SSL_CERT_FLAGS_OK) {
1130 if (GWEN_Buffer_GetUsedBytes(sbuf))
1131 GWEN_Buffer_AppendString(sbuf, "; ");
1132 GWEN_Buffer_AppendString(sbuf, I18N("The certificate is valid"));
1133 }
1134
1135 GWEN_SslCertDescr_SetStatusText(certDescr, GWEN_Buffer_GetStart(sbuf));
1136 GWEN_SslCertDescr_SetStatusFlags(certDescr, errFlags);
1137 GWEN_Buffer_free(sbuf);
1138
1139 #if 0
1140 if (1) {
1141 GWEN_DB_NODE *dbTest;
1142
1143 dbTest=GWEN_DB_Group_new("Cert");
1144 GWEN_SslCertDescr_toDb(certDescr, dbTest);
1145 GWEN_DB_Dump(dbTest, 2);
1146 GWEN_DB_Group_free(dbTest);
1147 }
1148 #endif
1149
1150 xio->peerCertDescr=certDescr;
1151 xio->peerCertFlags=errFlags;
1152
1153 return 0;
1154 }
1155
1156
1157
GWEN_SyncIo_Tls_Pull(gnutls_transport_ptr_t p,void * buf,size_t len)1158 ssize_t GWEN_SyncIo_Tls_Pull(gnutls_transport_ptr_t p, void *buf, size_t len)
1159 {
1160 GWEN_SYNCIO *sio;
1161 GWEN_SYNCIO_TLS *xio;
1162 GWEN_SYNCIO *baseIo;
1163 int rv;
1164
1165 sio=(GWEN_SYNCIO *) p;
1166 assert(sio);
1167 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1168 assert(xio);
1169
1170 DBG_VERBOUS(GWEN_LOGDOMAIN, "TLS PULL: %d bytes", (int)len);
1171 baseIo=GWEN_SyncIo_GetBaseIo(sio);
1172 assert(baseIo);
1173
1174 rv=GWEN_SyncIo_Read(baseIo, buf, len);
1175 if (rv<0) {
1176 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
1177 gnutls_transport_set_errno(xio->session, errno);
1178 return (ssize_t)-1;
1179 }
1180
1181 gnutls_transport_set_errno(xio->session, 0);
1182 DBG_VERBOUS(GWEN_LOGDOMAIN, "TLS PULL: returning %d bytes", rv);
1183 /*GWEN_Text_DumpString(buf, rv, 2);*/
1184 return rv;
1185 }
1186
1187
1188
GWEN_SyncIo_Tls_Push(gnutls_transport_ptr_t p,const void * buf,size_t len)1189 ssize_t GWEN_SyncIo_Tls_Push(gnutls_transport_ptr_t p, const void *buf, size_t len)
1190 {
1191 GWEN_SYNCIO *sio;
1192 GWEN_SYNCIO_TLS *xio;
1193 GWEN_SYNCIO *baseIo;
1194 int rv;
1195
1196 sio=(GWEN_SYNCIO *) p;
1197 assert(sio);
1198 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1199 assert(xio);
1200
1201 DBG_VERBOUS(GWEN_LOGDOMAIN, "TLS PUSH: %d bytes", (int)len);
1202 baseIo=GWEN_SyncIo_GetBaseIo(sio);
1203 assert(baseIo);
1204
1205 rv=GWEN_SyncIo_Write(baseIo, buf, len);
1206 if (rv<0) {
1207 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
1208 gnutls_transport_set_errno(xio->session, errno);
1209 return (ssize_t)-1;
1210 }
1211
1212 gnutls_transport_set_errno(xio->session, 0);
1213 DBG_VERBOUS(GWEN_LOGDOMAIN, "TLS PUSH: returning %d bytes", rv);
1214 /*GWEN_Text_DumpString(buf, rv, 2);*/
1215 return rv;
1216 }
1217
1218
1219
GWEN_SyncIo_Tls_ShowCipherInfo(GWEN_SYNCIO * sio)1220 void GWEN_SyncIo_Tls_ShowCipherInfo(GWEN_SYNCIO *sio)
1221 {
1222 GWEN_SYNCIO_TLS *xio;
1223 const char *s;
1224 gnutls_kx_algorithm_t kx;
1225 GWEN_BUFFER *cbuf;
1226 GWEN_BUFFER *sbuf;
1227
1228 assert(sio);
1229 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1230 assert(xio);
1231
1232 cbuf=GWEN_Buffer_new(0, 256, 0, 1);
1233 sbuf=GWEN_Buffer_new(0, 256, 0, 1);
1234
1235 /* protocol */
1236 s=gnutls_protocol_get_name(gnutls_protocol_get_version(xio->session));
1237 if (s && *s) {
1238 if (GWEN_Buffer_GetUsedBytes(cbuf))
1239 GWEN_Buffer_AppendString(cbuf, " ");
1240 GWEN_Buffer_AppendString(cbuf, "Protocol: ");
1241 GWEN_Buffer_AppendString(cbuf, s);
1242
1243 GWEN_Buffer_AppendString(sbuf, s);
1244 }
1245 GWEN_Buffer_AppendString(sbuf, ":");
1246
1247 /* key exchange algorithm */
1248 kx=gnutls_kx_get(xio->session);
1249 s=gnutls_kx_get_name(kx);
1250 if (s && *s) {
1251 if (GWEN_Buffer_GetUsedBytes(cbuf))
1252 GWEN_Buffer_AppendString(cbuf, " ");
1253 GWEN_Buffer_AppendString(cbuf, "Key exchange algorithm: ");
1254 GWEN_Buffer_AppendString(cbuf, s);
1255 GWEN_Buffer_AppendString(sbuf, s);
1256 }
1257 GWEN_Buffer_AppendString(sbuf, "-");
1258
1259 /* cipher */
1260 s=gnutls_cipher_get_name(gnutls_cipher_get(xio->session));
1261 if (s && *s) {
1262 if (GWEN_Buffer_GetUsedBytes(cbuf))
1263 GWEN_Buffer_AppendString(cbuf, " ");
1264 GWEN_Buffer_AppendString(cbuf, "cipher algorithm: ");
1265 GWEN_Buffer_AppendString(cbuf, s);
1266 GWEN_Buffer_AppendString(sbuf, s);
1267 }
1268 GWEN_Buffer_AppendString(sbuf, ":");
1269
1270 /* MAC algorithm */
1271 s=gnutls_mac_get_name(gnutls_mac_get(xio->session));
1272 if (s && *s) {
1273 if (GWEN_Buffer_GetUsedBytes(cbuf))
1274 GWEN_Buffer_AppendString(cbuf, " ");
1275 GWEN_Buffer_AppendString(cbuf, "MAC algorithm: ");
1276 GWEN_Buffer_AppendString(cbuf, s);
1277 GWEN_Buffer_AppendString(sbuf, s);
1278 }
1279
1280
1281 DBG_NOTICE(GWEN_LOGDOMAIN, "%s", GWEN_Buffer_GetStart(cbuf));
1282 GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Info, I18N("TLS: SSL-Ciphers negotiated: %s"), GWEN_Buffer_GetStart(sbuf));
1283 GWEN_Buffer_free(cbuf);
1284 GWEN_Buffer_free(sbuf);
1285
1286 /* possibly show warning */
1287 switch (gnutls_cipher_get(xio->session)) {
1288 case GNUTLS_CIPHER_ARCFOUR_128:
1289 case GNUTLS_CIPHER_3DES_CBC:
1290 case GNUTLS_CIPHER_AES_128_CBC:
1291 case GNUTLS_CIPHER_ARCFOUR_40:
1292 case GNUTLS_CIPHER_CAMELLIA_128_CBC:
1293 GWEN_Gui_ProgressLog(0, GWEN_LoggerLevel_Error, I18N("TLS: Warning - The server has chosen unsafe SSL-Ciphers!"));
1294 break;
1295 case GNUTLS_CIPHER_AES_256_CBC:
1296 case GNUTLS_CIPHER_CAMELLIA_256_CBC:
1297 case GNUTLS_CIPHER_RC2_40_CBC:
1298 case GNUTLS_CIPHER_DES_CBC:
1299 #ifdef GNUTLS_CIPHER_AES_192_CBC
1300 case GNUTLS_CIPHER_AES_192_CBC: /* new in gnutls-2.9.8, so i.e. not available in gnutls-2.8.x */
1301 #endif
1302 default:
1303 break;
1304 }
1305 }
1306
1307
1308
GWEN_SyncIo_Tls_Connect(GWEN_SYNCIO * sio)1309 int GWENHYWFAR_CB GWEN_SyncIo_Tls_Connect(GWEN_SYNCIO *sio)
1310 {
1311 GWEN_SYNCIO_TLS *xio;
1312 GWEN_SYNCIO *baseIo;
1313 int rv;
1314
1315 assert(sio);
1316 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1317 assert(xio);
1318
1319 baseIo=GWEN_SyncIo_GetBaseIo(sio);
1320 assert(baseIo);
1321
1322 if (GWEN_SyncIo_GetFlags(sio) & GWEN_SYNCIO_FLAGS_PASSIVE) {
1323 if (GWEN_SyncIo_GetStatus(baseIo)!=GWEN_SyncIo_Status_Connected) {
1324 DBG_ERROR(GWEN_LOGDOMAIN, "Base layer is not connected");
1325 return GWEN_ERROR_NOT_CONNECTED;
1326 }
1327 }
1328 else {
1329 DBG_INFO(GWEN_LOGDOMAIN, "Connecting base layer");
1330 rv=GWEN_SyncIo_Connect(baseIo);
1331 if (rv<0) {
1332 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
1333 return rv;
1334 }
1335 DBG_INFO(GWEN_LOGDOMAIN, "Base layer connected");
1336 }
1337
1338 rv=GWEN_SyncIo_Tls_Prepare(sio);
1339 if (rv<0) {
1340 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
1341 GWEN_SyncIo_Disconnect(baseIo);
1342 return rv;
1343 }
1344
1345 do {
1346 rv=gnutls_handshake(xio->session);
1347 }
1348 while (rv==GNUTLS_E_AGAIN || rv==GNUTLS_E_INTERRUPTED);
1349
1350 if (rv) {
1351 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_handshake: %d (%s) [%s]",
1352 rv, gnutls_strerror(rv), gnutls_error_is_fatal(rv)?"fatal":"non-fatal");
1353 if (rv==GNUTLS_E_UNEXPECTED_PACKET_LENGTH) {
1354 GWEN_Gui_ProgressLog(0,
1355 GWEN_LoggerLevel_Error,
1356 I18N("A TLS handshake error occurred. "
1357 "If you are using AqBanking you should "
1358 "consider enabling the option "
1359 "\"force SSLv3\" in the user settings "
1360 "dialog."));
1361 }
1362 else {
1363 GWEN_Gui_ProgressLog2(0,
1364 GWEN_LoggerLevel_Error,
1365 I18N("TLS Handshake Error: %d (%s)"),
1366 rv,
1367 gnutls_strerror(rv));
1368 }
1369 GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1370 GWEN_SyncIo_Tls_UndoPrepare(sio);
1371 GWEN_SyncIo_Disconnect(baseIo);
1372 return GWEN_ERROR_SSL;
1373 }
1374 else {
1375 /* show session info */
1376 GWEN_SyncIo_Tls_ShowCipherInfo(sio);
1377
1378 /* check certificate */
1379 GWEN_SyncIo_SubFlags(sio, GWEN_SYNCIO_TLS_FLAGS_SECURE);
1380 rv=GWEN_SyncIo_Tls_GetPeerCert(sio);
1381 if (rv<0) {
1382 if (GWEN_SyncIo_GetFlags(sio) & GWEN_SYNCIO_TLS_FLAGS_NEED_PEER_CERT) {
1383 DBG_ERROR(GWEN_LOGDOMAIN, "No peer certificate when needed, aborting connection");
1384 GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1385 GWEN_SyncIo_Tls_UndoPrepare(sio);
1386 GWEN_SyncIo_Disconnect(baseIo);
1387 return GWEN_ERROR_SSL_SECURITY;
1388 }
1389 else {
1390 DBG_INFO(GWEN_LOGDOMAIN, "SSL connected (insecure)");
1391 GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Connected);
1392 return 0;
1393 }
1394 }
1395 else {
1396 /* present cert to the user */
1397 rv=GWEN_SyncIo_Tls_CheckCert(sio, xio->peerCertDescr);
1398 if (rv<0) {
1399 DBG_ERROR(GWEN_LOGDOMAIN, "Peer cert not accepted (%d), aborting", rv);
1400 GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1401 GWEN_SyncIo_Tls_UndoPrepare(sio);
1402 GWEN_SyncIo_Disconnect(baseIo);
1403 return GWEN_ERROR_SSL_SECURITY;
1404 }
1405 else {
1406 DBG_INFO(GWEN_LOGDOMAIN, "SSL connected (secure)");
1407 GWEN_SyncIo_AddFlags(sio, GWEN_SYNCIO_TLS_FLAGS_SECURE);
1408 GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Connected);
1409 return 0;
1410 }
1411 }
1412 }
1413 }
1414
1415
1416
GWEN_SyncIo_Tls_Disconnect(GWEN_SYNCIO * sio)1417 int GWENHYWFAR_CB GWEN_SyncIo_Tls_Disconnect(GWEN_SYNCIO *sio)
1418 {
1419 GWEN_SYNCIO_TLS *xio;
1420 GWEN_SYNCIO *baseIo;
1421 int rv;
1422
1423 assert(sio);
1424 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1425 assert(xio);
1426
1427 baseIo=GWEN_SyncIo_GetBaseIo(sio);
1428 assert(baseIo);
1429
1430 if (GWEN_SyncIo_GetStatus(sio)!=GWEN_SyncIo_Status_Connected) {
1431 DBG_INFO(GWEN_LOGDOMAIN, "Not connected");
1432 GWEN_SyncIo_Tls_UndoPrepare(sio);
1433 GWEN_SyncIo_Disconnect(baseIo);
1434 return GWEN_ERROR_NOT_CONNECTED;
1435 }
1436
1437 do {
1438 rv=gnutls_bye(xio->session, GNUTLS_SHUT_RDWR);
1439 }
1440 while (rv==GNUTLS_E_AGAIN || rv==GNUTLS_E_INTERRUPTED);
1441
1442 if (rv) {
1443 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_bye: %d (%s)", rv, gnutls_strerror(rv));
1444 GWEN_Gui_ProgressLog2(0,
1445 GWEN_LoggerLevel_Info,
1446 I18N("Error on gnutls_bye: %d (%s)"),
1447 rv,
1448 gnutls_strerror(rv));
1449 GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1450 GWEN_SyncIo_Tls_UndoPrepare(sio);
1451 GWEN_SyncIo_Disconnect(baseIo);
1452 return GWEN_ERROR_SSL;
1453 }
1454
1455 GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1456 GWEN_SyncIo_Tls_UndoPrepare(sio);
1457 GWEN_SyncIo_Disconnect(baseIo);
1458 return 0;
1459 }
1460
1461
1462
GWEN_SyncIo_Tls_Read(GWEN_SYNCIO * sio,uint8_t * buffer,uint32_t size)1463 int GWENHYWFAR_CB GWEN_SyncIo_Tls_Read(GWEN_SYNCIO *sio,
1464 uint8_t *buffer,
1465 uint32_t size)
1466 {
1467 GWEN_SYNCIO_TLS *xio;
1468 GWEN_SYNCIO *baseIo;
1469 int rv;
1470
1471 assert(sio);
1472 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1473 assert(xio);
1474
1475 baseIo=GWEN_SyncIo_GetBaseIo(sio);
1476 assert(baseIo);
1477
1478 if (GWEN_SyncIo_GetStatus(sio)!=GWEN_SyncIo_Status_Connected) {
1479 DBG_INFO(GWEN_LOGDOMAIN, "Not connected");
1480 GWEN_SyncIo_Tls_UndoPrepare(sio);
1481 GWEN_SyncIo_Disconnect(baseIo);
1482 return GWEN_ERROR_NOT_CONNECTED;
1483 }
1484
1485 do {
1486 rv=gnutls_record_recv(xio->session, buffer, size);
1487 }
1488 while (rv==GNUTLS_E_AGAIN || rv==GNUTLS_E_INTERRUPTED);
1489
1490 if (rv<0) {
1491 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_record_recv: %d (%s)", rv, gnutls_strerror(rv));
1492 #if 0
1493 GWEN_Gui_ProgressLog2(0,
1494 GWEN_LoggerLevel_Error,
1495 I18N("Error on gnutls_record_recv: %d (%s)"),
1496 rv,
1497 gnutls_strerror(rv));
1498 #endif
1499 GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1500 GWEN_SyncIo_Tls_UndoPrepare(sio);
1501 GWEN_SyncIo_Disconnect(baseIo);
1502 #ifdef GNUTLS_E_PREMATURE_TERMINATION
1503 if (rv==GNUTLS_E_PREMATURE_TERMINATION) {
1504 if (GWEN_SyncIo_GetFlags(sio) & GWEN_SYNCIO_TLS_FLAGS_IGN_PREMATURE_CLOSE) {
1505 DBG_ERROR(GWEN_LOGDOMAIN, "Detected premature disconnect by server (violates specs!), ignoring.");
1506 return 0; /* report EOF */
1507 }
1508 else {
1509 DBG_ERROR(GWEN_LOGDOMAIN, "Detected premature disconnect by server (violates specs!)");
1510 return GWEN_ERROR_SSL_PREMATURE_CLOSE;
1511 }
1512 }
1513 #endif
1514 return GWEN_ERROR_SSL;
1515 }
1516
1517 #ifdef GWEN_TLS_DEBUG
1518 DBG_ERROR(0, "Received this:");
1519 GWEN_Text_DumpString((const char *) buffer, rv, 2);
1520 #endif
1521
1522 return rv;
1523 }
1524
1525
1526
GWEN_SyncIo_Tls_Write(GWEN_SYNCIO * sio,const uint8_t * buffer,uint32_t size)1527 int GWENHYWFAR_CB GWEN_SyncIo_Tls_Write(GWEN_SYNCIO *sio,
1528 const uint8_t *buffer,
1529 uint32_t size)
1530 {
1531 GWEN_SYNCIO_TLS *xio;
1532 GWEN_SYNCIO *baseIo;
1533 int rv;
1534
1535 assert(sio);
1536 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_TLS, sio);
1537 assert(xio);
1538
1539 #ifdef GWEN_TLS_DEBUG
1540 DBG_ERROR(0, "Sending this:");
1541 GWEN_Text_DumpString((const char *) buffer, size, 2);
1542 #endif
1543
1544 baseIo=GWEN_SyncIo_GetBaseIo(sio);
1545 assert(baseIo);
1546
1547 if (GWEN_SyncIo_GetStatus(sio)!=GWEN_SyncIo_Status_Connected) {
1548 DBG_INFO(GWEN_LOGDOMAIN, "Not connected");
1549 GWEN_SyncIo_Tls_UndoPrepare(sio);
1550 GWEN_SyncIo_Disconnect(baseIo);
1551 return GWEN_ERROR_NOT_CONNECTED;
1552 }
1553
1554 do {
1555 rv=gnutls_record_send(xio->session, buffer, size);
1556 }
1557 while (rv==GNUTLS_E_AGAIN || rv==GNUTLS_E_INTERRUPTED);
1558
1559 if (rv<0) {
1560 DBG_ERROR(GWEN_LOGDOMAIN, "gnutls_record_send: %d (%s)", rv, gnutls_strerror(rv));
1561 GWEN_Gui_ProgressLog2(0,
1562 GWEN_LoggerLevel_Error,
1563 I18N("Error on gnutls_record_send: %d (%s)"),
1564 rv,
1565 gnutls_strerror(rv));
1566 GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected);
1567 GWEN_SyncIo_Tls_UndoPrepare(sio);
1568 GWEN_SyncIo_Disconnect(baseIo);
1569 return GWEN_ERROR_SSL;
1570 }
1571
1572 return rv;
1573 }
1574
1575
1576
1577
1578
1579
1580
1581