1 /***************************************************************************
2 begin : Fri Apr 21 2017
3 copyright : (C) 2019 by Martin Preuss
4 email : martin@libchipcard.de
5
6 ***************************************************************************
7 * This file is part of the project "AqBanking". *
8 * Please see toplevel file COPYING of that project for license details. *
9 ***************************************************************************/
10
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 #endif
14
15 #include "siotlsext_p.h"
16
17 #include <gwenhywfar/gui.h>
18 #include <gwenhywfar/syncio_tls.h>
19 #include <gwenhywfar/debug.h>
20
21
22
23
24 /* ------------------------------------------------------------------------------------------------
25 * forward declarations
26 * ------------------------------------------------------------------------------------------------
27 */
28
29 /** @return 0 if undecided, 1 if acceptable, negative on error */
30 static int _checkCert(GWEN_SYNCIO *sio, const GWEN_SSLCERTDESCR *cert);
31 static int _checkStoredUserCerts(AB_USER *u, const GWEN_SSLCERTDESCR *cert);
32 static int _checkAgainstStoredCert(const GWEN_SSLCERTDESCR *cert, GWEN_SSLCERTDESCR *storedCert, GWEN_DB_NODE *dbC);
33 static int _checkStoredUserResponse(GWEN_DB_NODE *dbC, const char *sFingerprint);
34 static int _checkAutoDecision(const GWEN_SSLCERTDESCR *cert);
35 static int _askUserAboutCert(GWEN_SYNCIO *sio, const GWEN_SSLCERTDESCR *cert);
36 static void _storeAccessDate(GWEN_DB_NODE *dbCert);
37 static void _storeCertAndUserResponseInUser(AB_USER *u, const GWEN_SSLCERTDESCR *cert, int response);
38
39
40 /* ------------------------------------------------------------------------------------------------
41 * implementations
42 * ------------------------------------------------------------------------------------------------
43 */
44
45
GWEN_INHERIT(GWEN_SYNCIO,AB_SIOTLS_EXT)46 GWEN_INHERIT(GWEN_SYNCIO, AB_SIOTLS_EXT)
47
48
49
50 void AB_SioTlsExt_Extend(GWEN_SYNCIO *sio, AB_USER *u)
51 {
52 AB_SIOTLS_EXT *xsio;
53
54 GWEN_NEW_OBJECT(AB_SIOTLS_EXT, xsio);
55 GWEN_INHERIT_SETDATA(GWEN_SYNCIO, AB_SIOTLS_EXT, sio, xsio, AB_SioTlsExt_FreeData);
56
57 /* set data */
58 xsio->user=u;
59
60 /* set callbacks */
61 xsio->oldCheckCertFn=GWEN_SyncIo_Tls_SetCheckCertFn(sio, AB_SioTlsExt_CheckCert);
62 }
63
64
65
AB_SioTlsExt_Unextend(GWEN_SYNCIO * sio)66 void AB_SioTlsExt_Unextend(GWEN_SYNCIO *sio)
67 {
68 AB_SIOTLS_EXT *xsio;
69
70 assert(sio);
71 xsio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, AB_SIOTLS_EXT, sio);
72 assert(xsio);
73
74 /* reset callbacks which point into AB_SioTlsExt */
75 GWEN_SyncIo_Tls_SetCheckCertFn(sio, xsio->oldCheckCertFn);
76
77 /* unlink from GWEN_SYNCIO object */
78 DBG_INFO(AQBANKING_LOGDOMAIN, "Unlinking SIO from banking object");
79 GWEN_INHERIT_UNLINK(GWEN_SYNCIO, AB_SIOTLS_EXT, sio);
80 }
81
82
83
AB_SioTlsExt_FreeData(void * bp,void * p)84 void GWENHYWFAR_CB AB_SioTlsExt_FreeData(void *bp, void *p)
85 {
86 AB_SIOTLS_EXT *xsio;
87
88 xsio=(AB_SIOTLS_EXT *) p;
89 assert(xsio);
90 GWEN_FREE_OBJECT(xsio);
91 }
92
93
94
95 /*
96 AB_USER *AB_SioTlsExt_GetUser(const GWEN_SYNCIO *sio) {
97 AB_SIOTLS_EXT *xsio;
98
99 assert(sio);
100 xsio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, AB_SIOTLS_EXT, sio);
101 assert(xsio);
102
103 return xsio->user;
104 }
105 */
106
107
108
109
110
AB_SioTlsExt_CheckCert(GWEN_SYNCIO * sio,const GWEN_SSLCERTDESCR * cert)111 int GWENHYWFAR_CB AB_SioTlsExt_CheckCert(GWEN_SYNCIO *sio, const GWEN_SSLCERTDESCR *cert)
112 {
113 int rv;
114
115 rv=_checkCert(sio, cert);
116 if (rv==1) {
117 DBG_INFO(AQBANKING_LOGDOMAIN, "Cert accepted.");
118 return 0;
119 }
120 else if (rv<0) {
121 DBG_INFO(AQBANKING_LOGDOMAIN, "here (%d)", rv);
122 return rv;
123 }
124
125 /* undecided, abort */
126 DBG_INFO(AQBANKING_LOGDOMAIN, "Undecided, assuming abort");
127 return GWEN_ERROR_USER_ABORTED;
128 }
129
130
131
_checkCert(GWEN_SYNCIO * sio,const GWEN_SSLCERTDESCR * cert)132 int _checkCert(GWEN_SYNCIO *sio, const GWEN_SSLCERTDESCR *cert)
133 {
134 AB_SIOTLS_EXT *xsio;
135 int rv;
136
137 assert(sio);
138 xsio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, AB_SIOTLS_EXT, sio);
139 assert(xsio);
140
141 assert(xsio->user);
142
143 rv=_checkStoredUserCerts(xsio->user, cert);
144 if (rv!=0) {
145 DBG_INFO(AQBANKING_LOGDOMAIN, "here (%d)", rv);
146 return rv;
147 }
148
149 rv=_checkAutoDecision(cert);
150 if (rv!=0) {
151 DBG_INFO(AQBANKING_LOGDOMAIN, "here (%d)", rv);
152 return rv;
153 }
154
155 rv=_askUserAboutCert(sio, cert);
156 if (rv!=0) {
157 DBG_INFO(AQBANKING_LOGDOMAIN, "here (%d)", rv);
158 _storeCertAndUserResponseInUser(xsio->user, cert, rv);
159 return rv;
160 }
161
162 /* undecided */
163 return 0;
164 }
165
166
167
_checkStoredUserCerts(AB_USER * u,const GWEN_SSLCERTDESCR * cert)168 int _checkStoredUserCerts(AB_USER *u, const GWEN_SSLCERTDESCR *cert)
169 {
170 GWEN_DB_NODE *dbCerts;
171 GWEN_DB_NODE *dbC;
172 const char *sFingerprint;
173
174 assert(u);
175
176 sFingerprint=GWEN_SslCertDescr_GetFingerPrint(cert);
177
178 /* get or create user-based certificate store */
179 dbCerts=AB_User_GetCertDb(u);
180 if (dbCerts==NULL) {
181 dbCerts=GWEN_DB_Group_new("certs");
182 AB_User_SetCertDb(u, dbCerts);
183 }
184
185 /* find group which contains the certificate with the given fingerprint */
186 dbC=GWEN_DB_GetGroup(dbCerts, GWEN_PATH_FLAGS_PATHMUSTEXIST, sFingerprint);
187 if (dbC) {
188 GWEN_SSLCERTDESCR *storedCert;
189 int rv;
190
191 /* there is such a group, read stored certificate */
192 storedCert=GWEN_SslCertDescr_fromDb(dbC);
193 if (storedCert==NULL) {
194 DBG_ERROR(AQBANKING_LOGDOMAIN, "Unable to load stored certificate \"%s\"", sFingerprint);
195 return GWEN_ERROR_INTERNAL;
196 }
197
198 _storeAccessDate(dbC);
199
200 rv=_checkAgainstStoredCert(cert, storedCert, dbC);
201 DBG_INFO(AQBANKING_LOGDOMAIN, "here (%d)", rv);
202 GWEN_SslCertDescr_free(storedCert);
203 return rv;
204 } /* if dbC */
205
206 /* undecided */
207 return 0;
208 }
209
210
211
_checkAgainstStoredCert(const GWEN_SSLCERTDESCR * cert,GWEN_SSLCERTDESCR * storedCert,GWEN_DB_NODE * dbC)212 int _checkAgainstStoredCert(const GWEN_SSLCERTDESCR *cert, GWEN_SSLCERTDESCR *storedCert, GWEN_DB_NODE *dbC)
213 {
214 const char *sFingerprint;
215 const char *sStatus;
216 uint32_t iStatus;
217 uint32_t iStoredStatus;
218
219 sFingerprint=GWEN_SslCertDescr_GetFingerPrint(cert);
220 sStatus=GWEN_SslCertDescr_GetStatusText(cert);
221 iStatus=GWEN_SslCertDescr_GetStatusFlags(cert);
222
223 /* get status of stored certificate */
224 iStoredStatus=GWEN_SslCertDescr_GetStatusFlags(storedCert);
225
226 /* compare status texts */
227 if (iStatus==iStoredStatus) {
228 /* found matching cert, return user's previous answer */
229 DBG_NOTICE(AQBANKING_LOGDOMAIN, "Found matching certificate \"%s\" with same status", sFingerprint);
230 return _checkStoredUserResponse(dbC, sFingerprint);
231 } /* if same status */
232 else {
233 DBG_NOTICE(AQBANKING_LOGDOMAIN,
234 "Status for certificate \%s\" has changed to \"%s\" (%08x->%08x), need to present",
235 sFingerprint, sStatus,
236 iStoredStatus, iStatus);
237 }
238 return 0;
239 }
240
241
242
_checkStoredUserResponse(GWEN_DB_NODE * dbC,const char * sFingerprint)243 int _checkStoredUserResponse(GWEN_DB_NODE *dbC, const char *sFingerprint)
244 {
245 int rv;
246
247 rv=GWEN_DB_GetIntValue(dbC, "userResponse", 0, -1);
248 if (rv==0) {
249 /* last user response was to accept the certificate so we're done */
250 DBG_NOTICE(AQBANKING_LOGDOMAIN, "Automatically accepting certificate [%s]", sFingerprint);
251 return 1;
252 }
253 else {
254 /* last user response was to reject the certificate so we're done */
255 /* DBG_NOTICE(AQBANKING_LOGDOMAIN, "Automatically rejecting certificate [%s] (%d)", sFingerprint, rv);
256 return rv; */
257 /* undecided (ask user again) */
258 return 0;
259 }
260 }
261
262
263
_checkAutoDecision(const GWEN_SSLCERTDESCR * cert)264 int _checkAutoDecision(const GWEN_SSLCERTDESCR *cert)
265 {
266 GWEN_GUI *gui;
267 const char *sFingerprint;
268
269 sFingerprint=GWEN_SslCertDescr_GetFingerPrint(cert);
270
271 /* at this point the certificate was either not found or its status has changed,
272 * possibly ask the user how to preceed */
273 gui=GWEN_Gui_GetGui();
274 assert(gui);
275
276 if (GWEN_Gui_GetFlags(gui) & GWEN_GUI_FLAGS_NONINTERACTIVE) {
277 uint32_t fl;
278
279 fl=GWEN_SslCertDescr_GetStatusFlags(cert);
280 if (fl==GWEN_SSL_CERT_FLAGS_OK) {
281 if (GWEN_Gui_GetFlags(gui) & GWEN_GUI_FLAGS_ACCEPTVALIDCERTS) {
282 DBG_NOTICE(AQBANKING_LOGDOMAIN, "Automatically accepting valid new certificate [%s]", sFingerprint);
283 return 1;
284 }
285 else {
286 DBG_NOTICE(AQBANKING_LOGDOMAIN, "Automatically rejecting certificate [%s] (noninteractive)", sFingerprint);
287 GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Warning,
288 "Automatically rejecting certificate [%s] (noninteractive)",
289 sFingerprint);
290 return GWEN_ERROR_USER_ABORTED;
291 }
292 } /* if cert is valid */
293 else {
294 if (GWEN_Gui_GetFlags(gui) & GWEN_GUI_FLAGS_REJECTINVALIDCERTS) {
295 DBG_NOTICE(AQBANKING_LOGDOMAIN, "Automatically rejecting invalid certificate [%s] (noninteractive)", sFingerprint);
296 GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Warning,
297 "Automatically rejecting invalid certificate [%s] (noninteractive)",
298 sFingerprint);
299 return GWEN_ERROR_USER_ABORTED;
300 }
301 }
302 } /* if non-interactive */
303
304 /* undecided */
305 return 0;
306 }
307
308
309
_askUserAboutCert(GWEN_SYNCIO * sio,const GWEN_SSLCERTDESCR * cert)310 int _askUserAboutCert(GWEN_SYNCIO *sio, const GWEN_SSLCERTDESCR *cert)
311 {
312 AB_SIOTLS_EXT *xsio;
313
314 assert(sio);
315 xsio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, AB_SIOTLS_EXT, sio);
316 assert(xsio);
317
318 /* use previous checkCert function, which normally presents the certificate
319 * to the user and asks for a response */
320 if (xsio->oldCheckCertFn) {
321 int rv;
322
323 /* get user response */
324 rv=xsio->oldCheckCertFn(sio, cert);
325 if (rv==0)
326 return 1;
327 else {
328 DBG_INFO(AQBANKING_LOGDOMAIN, "here (%d)", rv);
329 return rv;
330 }
331 }
332 else {
333 DBG_NOTICE(AQBANKING_LOGDOMAIN, "Internal error: No previous checkCert function");
334 return GWEN_ERROR_INTERNAL;
335 }
336 }
337
338
339
_storeCertAndUserResponseInUser(AB_USER * u,const GWEN_SSLCERTDESCR * cert,int response)340 void _storeCertAndUserResponseInUser(AB_USER *u, const GWEN_SSLCERTDESCR *cert, int response)
341 {
342 GWEN_DB_NODE *dbCerts;
343 GWEN_DB_NODE *dbC;
344 const char *sFingerprint;
345 const char *sStatus;
346
347 sFingerprint=GWEN_SslCertDescr_GetFingerPrint(cert);
348 sStatus=GWEN_SslCertDescr_GetStatusText(cert);
349
350 dbCerts=AB_User_GetCertDb(u);
351 assert(dbCerts);
352
353 /* store certificate in database */
354 dbC=GWEN_DB_GetGroup(dbCerts, GWEN_DB_FLAGS_OVERWRITE_GROUPS, sFingerprint);
355 assert(dbC);
356 GWEN_SslCertDescr_toDb(cert, dbC);
357
358 /* store user response */
359 GWEN_DB_SetIntValue(dbC, GWEN_DB_FLAGS_OVERWRITE_VARS, "userResponse", (response==1)?0:response);
360 DBG_NOTICE(AQBANKING_LOGDOMAIN,
361 "User response to presentation of cert \"%s\" (%s): %d",
362 sFingerprint, sStatus, (response==1)?0:response);
363
364 _storeAccessDate(dbC);
365 }
366
367
368
_storeAccessDate(GWEN_DB_NODE * dbCert)369 void _storeAccessDate(GWEN_DB_NODE *dbCert)
370 {
371 GWEN_DATE *dt;
372
373 dt=GWEN_Date_CurrentDate();
374 GWEN_DB_SetCharValue(dbCert, GWEN_DB_FLAGS_OVERWRITE_VARS, "lastAccessDate", GWEN_Date_GetString(dt));
375 GWEN_Date_free(dt);
376 }
377
378