1 | /* |
2 | * Copyright (C) 2003-2007 Justin Karneges <justin@affinix.com> |
3 | * Copyright (C) 2004,2005 Brad Hards <bradh@frogmouth.net> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2.1 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library; if not, write to the Free Software |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
18 | * 02110-1301 USA |
19 | * |
20 | */ |
21 | |
22 | #include "qca_securelayer.h" |
23 | |
24 | #include "qca_safeobj.h" |
25 | #include "qca_safetimer.h" |
26 | #include "qcaprovider.h" |
27 | |
28 | #include <QMetaMethod> |
29 | #include <QPointer> |
30 | |
31 | namespace QCA { |
32 | |
33 | Provider::Context *getContext(const QString &type, const QString &provider); |
34 | |
35 | enum ResetMode |
36 | { |
37 | ResetSession = 0, |
38 | ResetSessionAndData = 1, |
39 | ResetAll = 2 |
40 | }; |
41 | |
42 | //---------------------------------------------------------------------------- |
43 | // LayerTracker |
44 | //---------------------------------------------------------------------------- |
45 | class LayerTracker |
46 | { |
47 | private: |
48 | struct Item |
49 | { |
50 | int plain; |
51 | qint64 encoded; |
52 | }; |
53 | |
54 | int p; |
55 | QList<Item> list; |
56 | |
57 | public: |
58 | LayerTracker() |
59 | { |
60 | p = 0; |
61 | } |
62 | |
63 | void reset() |
64 | { |
65 | p = 0; |
66 | list.clear(); |
67 | } |
68 | |
69 | void addPlain(int plain) |
70 | { |
71 | p += plain; |
72 | } |
73 | |
74 | void specifyEncoded(int encoded, int plain) |
75 | { |
76 | // can't specify more bytes than we have |
77 | if (plain > p) |
78 | plain = p; |
79 | p -= plain; |
80 | Item i; |
81 | i.plain = plain; |
82 | i.encoded = encoded; |
83 | list += i; |
84 | } |
85 | |
86 | int finished(qint64 encoded) |
87 | { |
88 | int plain = 0; |
89 | for (QList<Item>::Iterator it = list.begin(); it != list.end();) { |
90 | Item &i = *it; |
91 | |
92 | // not enough? |
93 | if (encoded < i.encoded) { |
94 | i.encoded -= encoded; |
95 | break; |
96 | } |
97 | |
98 | encoded -= i.encoded; |
99 | plain += i.plain; |
100 | it = list.erase(pos: it); |
101 | } |
102 | return plain; |
103 | } |
104 | }; |
105 | |
106 | //---------------------------------------------------------------------------- |
107 | // SecureLayer |
108 | //---------------------------------------------------------------------------- |
109 | SecureLayer::SecureLayer(QObject *parent) |
110 | : QObject(parent) |
111 | { |
112 | } |
113 | |
114 | bool SecureLayer::isClosable() const |
115 | { |
116 | return false; |
117 | } |
118 | |
119 | void SecureLayer::close() |
120 | { |
121 | } |
122 | |
123 | QByteArray SecureLayer::readUnprocessed() |
124 | { |
125 | return QByteArray(); |
126 | } |
127 | |
128 | //---------------------------------------------------------------------------- |
129 | // TLSSession |
130 | //---------------------------------------------------------------------------- |
131 | TLSSession::TLSSession() |
132 | { |
133 | } |
134 | |
135 | TLSSession::TLSSession(const TLSSession &from) |
136 | : Algorithm(from) |
137 | { |
138 | } |
139 | |
140 | TLSSession::~TLSSession() |
141 | { |
142 | } |
143 | |
144 | TLSSession &TLSSession::operator=(const TLSSession &from) |
145 | { |
146 | Algorithm::operator=(from); |
147 | return *this; |
148 | } |
149 | |
150 | bool TLSSession::isNull() const |
151 | { |
152 | return (!context() ? true : false); |
153 | } |
154 | |
155 | //---------------------------------------------------------------------------- |
156 | // TLS |
157 | //---------------------------------------------------------------------------- |
158 | class TLS::Private : public QObject |
159 | { |
160 | Q_OBJECT |
161 | public: |
162 | enum |
163 | { |
164 | OpStart, |
165 | OpUpdate |
166 | }; |
167 | |
168 | enum State |
169 | { |
170 | Inactive, |
171 | Initializing, |
172 | Handshaking, |
173 | Connected, |
174 | Closing |
175 | }; |
176 | |
177 | class Action |
178 | { |
179 | public: |
180 | enum Type |
181 | { |
182 | ReadyRead, |
183 | ReadyReadOutgoing, |
184 | Handshaken, |
185 | Close, |
186 | CheckPeerCertificate, |
187 | CertificateRequested, |
188 | HostNameReceived |
189 | }; |
190 | |
191 | int type; |
192 | |
193 | Action(int _type) |
194 | : type(_type) |
195 | { |
196 | } |
197 | }; |
198 | |
199 | TLS *q; |
200 | TLSContext *c; |
201 | TLS::Mode mode; |
202 | |
203 | // signal connected flags |
204 | bool connect_hostNameReceived; |
205 | bool connect_certificateRequested; |
206 | bool connect_peerCertificateAvailable; |
207 | bool connect_handshaken; |
208 | |
209 | // persistent settings (survives ResetSessionAndData) |
210 | CertificateChain localCert; |
211 | PrivateKey localKey; |
212 | CertificateCollection trusted; |
213 | bool con_ssfMode; |
214 | int con_minSSF, con_maxSSF; |
215 | QStringList con_cipherSuites; |
216 | bool tryCompress; |
217 | int packet_mtu; |
218 | QList<CertificateInfoOrdered> issuerList; |
219 | TLSSession session; |
220 | |
221 | // session |
222 | State state; |
223 | bool blocked; |
224 | bool server; |
225 | QString host; |
226 | TLSContext::SessionInfo sessionInfo; |
227 | SafeTimer actionTrigger; |
228 | int op; |
229 | QList<Action> actionQueue; |
230 | bool need_update; |
231 | bool maybe_input; |
232 | bool emitted_hostNameReceived; |
233 | bool emitted_certificateRequested; |
234 | bool emitted_peerCertificateAvailable; |
235 | |
236 | // data (survives ResetSession) |
237 | CertificateChain peerCert; |
238 | Validity peerValidity; |
239 | bool hostMismatch; |
240 | Error errorCode; |
241 | |
242 | // stream i/o |
243 | QByteArray in, out; |
244 | QByteArray to_net, from_net; |
245 | QByteArray unprocessed; |
246 | int out_pending; |
247 | int to_net_encoded; |
248 | LayerTracker layer; |
249 | |
250 | // datagram i/o |
251 | QList<QByteArray> packet_in, packet_out; |
252 | QList<QByteArray> packet_to_net, packet_from_net; |
253 | int packet_out_pending; // packet count |
254 | QList<int> packet_to_net_encoded; |
255 | |
256 | Private(TLS *_q, TLS::Mode _mode) |
257 | : QObject(_q) |
258 | , q(_q) |
259 | , mode(_mode) |
260 | , actionTrigger(this) |
261 | { |
262 | // c is 0 during initial reset, so we don't redundantly reset it |
263 | c = nullptr; |
264 | connect_hostNameReceived = false; |
265 | connect_certificateRequested = false; |
266 | connect_peerCertificateAvailable = false; |
267 | connect_handshaken = false; |
268 | server = false; |
269 | |
270 | connect(sender: &actionTrigger, signal: &SafeTimer::timeout, context: this, slot: &Private::doNextAction); |
271 | actionTrigger.setSingleShot(true); |
272 | |
273 | reset(mode: ResetAll); |
274 | |
275 | c = static_cast<TLSContext *>(q->context()); |
276 | |
277 | // parent the context to us, so that moveToThread works |
278 | c->setParent(this); |
279 | |
280 | connect(sender: c, signal: &TLSContext::resultsReady, context: this, slot: &Private::tls_resultsReady); |
281 | connect(sender: c, signal: &TLSContext::dtlsTimeout, context: this, slot: &Private::tls_dtlsTimeout); |
282 | } |
283 | |
284 | ~Private() override |
285 | { |
286 | // context is owned by Algorithm, unparent so we don't double-delete |
287 | c->setParent(nullptr); |
288 | } |
289 | |
290 | void reset(ResetMode mode) |
291 | { |
292 | if (c) |
293 | c->reset(); |
294 | |
295 | // if we reset while in client mode, then clear this list |
296 | // (it should only persist when used for server mode) |
297 | if (!server) |
298 | issuerList.clear(); |
299 | |
300 | state = Inactive; |
301 | blocked = false; |
302 | server = false; |
303 | host = QString(); |
304 | sessionInfo = TLSContext::SessionInfo(); |
305 | actionTrigger.stop(); |
306 | op = -1; |
307 | actionQueue.clear(); |
308 | need_update = false; |
309 | maybe_input = false; |
310 | emitted_hostNameReceived = false; |
311 | emitted_certificateRequested = false; |
312 | emitted_peerCertificateAvailable = false; |
313 | |
314 | out.clear(); |
315 | out_pending = 0; |
316 | packet_out.clear(); |
317 | packet_out_pending = 0; |
318 | |
319 | if (mode >= ResetSessionAndData) { |
320 | peerCert = CertificateChain(); |
321 | peerValidity = ErrorValidityUnknown; |
322 | hostMismatch = false; |
323 | errorCode = (TLS::Error)-1; |
324 | |
325 | in.clear(); |
326 | to_net.clear(); |
327 | from_net.clear(); |
328 | unprocessed.clear(); |
329 | to_net_encoded = 0; |
330 | layer.reset(); |
331 | |
332 | packet_in.clear(); |
333 | packet_to_net.clear(); |
334 | packet_from_net.clear(); |
335 | packet_to_net_encoded.clear(); |
336 | } |
337 | |
338 | if (mode >= ResetAll) { |
339 | localCert = CertificateChain(); |
340 | localKey = PrivateKey(); |
341 | trusted = CertificateCollection(); |
342 | con_ssfMode = true; |
343 | con_minSSF = 128; |
344 | con_maxSSF = -1; |
345 | con_cipherSuites = QStringList(); |
346 | tryCompress = false; |
347 | packet_mtu = -1; |
348 | issuerList.clear(); |
349 | session = TLSSession(); |
350 | } |
351 | } |
352 | |
353 | void start(bool serverMode) |
354 | { |
355 | state = Initializing; |
356 | server = serverMode; |
357 | |
358 | c->setup(serverMode, hostName: host, compress: tryCompress); |
359 | |
360 | if (con_ssfMode) |
361 | c->setConstraints(minSSF: con_minSSF, maxSSF: con_maxSSF); |
362 | else |
363 | c->setConstraints(con_cipherSuites); |
364 | |
365 | c->setCertificate(cert: localCert, key: localKey); |
366 | c->setTrustedCertificates(trusted); |
367 | if (serverMode) |
368 | c->setIssuerList(issuerList); |
369 | if (!session.isNull()) { |
370 | TLSSessionContext *sc = static_cast<TLSSessionContext *>(session.context()); |
371 | c->setSessionId(*sc); |
372 | } |
373 | c->setMTU(packet_mtu); |
374 | |
375 | QCA_logTextMessage(QStringLiteral("tls[%1]: c->start()" ).arg(q->objectName()), Logger::Information); |
376 | op = OpStart; |
377 | c->start(); |
378 | } |
379 | |
380 | void close() |
381 | { |
382 | QCA_logTextMessage(QStringLiteral("tls[%1]: close" ).arg(q->objectName()), Logger::Information); |
383 | |
384 | if (state != Connected) |
385 | return; |
386 | |
387 | state = Closing; |
388 | c->shutdown(); |
389 | } |
390 | |
391 | void continueAfterStep() |
392 | { |
393 | QCA_logTextMessage(QStringLiteral("tls[%1]: continueAfterStep" ).arg(q->objectName()), Logger::Information); |
394 | |
395 | if (!blocked) |
396 | return; |
397 | |
398 | blocked = false; |
399 | update(); |
400 | } |
401 | |
402 | void processNextAction() |
403 | { |
404 | if (actionQueue.isEmpty()) { |
405 | if (need_update) { |
406 | QCA_logTextMessage(QStringLiteral("tls[%1]: need_update" ).arg(q->objectName()), Logger::Information); |
407 | update(); |
408 | } |
409 | return; |
410 | } |
411 | |
412 | const Action a = actionQueue.takeFirst(); |
413 | |
414 | // set up for the next one, if necessary |
415 | if (!actionQueue.isEmpty() || need_update) { |
416 | if (!actionTrigger.isActive()) |
417 | actionTrigger.start(); |
418 | } |
419 | |
420 | if (a.type == Action::ReadyRead) { |
421 | emit q->readyRead(); |
422 | } else if (a.type == Action::ReadyReadOutgoing) { |
423 | emit q->readyReadOutgoing(); |
424 | } else if (a.type == Action::Handshaken) { |
425 | state = Connected; |
426 | |
427 | // write any app data waiting during handshake |
428 | if (!out.isEmpty()) { |
429 | need_update = true; |
430 | if (!actionTrigger.isActive()) |
431 | actionTrigger.start(); |
432 | } |
433 | |
434 | QCA_logTextMessage(QStringLiteral("tls[%1]: handshaken" ).arg(q->objectName()), Logger::Information); |
435 | |
436 | if (connect_handshaken) { |
437 | blocked = true; |
438 | emit q->handshaken(); |
439 | } |
440 | } else if (a.type == Action::Close) { |
441 | unprocessed = c->unprocessed(); |
442 | reset(mode: ResetSession); |
443 | emit q->closed(); |
444 | } else if (a.type == Action::CheckPeerCertificate) { |
445 | peerCert = c->peerCertificateChain(); |
446 | if (!peerCert.isEmpty()) { |
447 | peerValidity = c->peerCertificateValidity(); |
448 | if (peerValidity == ValidityGood && !host.isEmpty() && !peerCert.primary().matchesHostName(host)) |
449 | hostMismatch = true; |
450 | } |
451 | |
452 | if (connect_peerCertificateAvailable) { |
453 | blocked = true; |
454 | emitted_peerCertificateAvailable = true; |
455 | emit q->peerCertificateAvailable(); |
456 | } |
457 | } else if (a.type == Action::CertificateRequested) { |
458 | issuerList = c->issuerList(); |
459 | if (connect_certificateRequested) { |
460 | blocked = true; |
461 | emitted_certificateRequested = true; |
462 | emit q->certificateRequested(); |
463 | } |
464 | } else if (a.type == Action::HostNameReceived) { |
465 | if (connect_hostNameReceived) { |
466 | blocked = true; |
467 | emitted_hostNameReceived = true; |
468 | emit q->hostNameReceived(); |
469 | } |
470 | } |
471 | } |
472 | |
473 | void update() |
474 | { |
475 | QCA_logTextMessage(QStringLiteral("tls[%1]: update" ).arg(q->objectName()), Logger::Information); |
476 | |
477 | if (blocked) { |
478 | QCA_logTextMessage(QStringLiteral("tls[%1]: ignoring update while blocked" ).arg(q->objectName()), |
479 | Logger::Information); |
480 | return; |
481 | } |
482 | |
483 | if (!actionQueue.isEmpty()) { |
484 | QCA_logTextMessage(QStringLiteral("tls[%1]: ignoring update while processing actions" ).arg(q->objectName()), |
485 | Logger::Information); |
486 | need_update = true; |
487 | return; |
488 | } |
489 | |
490 | // only allow one operation at a time |
491 | if (op != -1) { |
492 | QCA_logTextMessage(QStringLiteral("tls[%1]: ignoring update while operation active" ).arg(q->objectName()), |
493 | Logger::Information); |
494 | need_update = true; |
495 | return; |
496 | } |
497 | |
498 | need_update = false; |
499 | |
500 | QByteArray arg_from_net, arg_from_app; |
501 | |
502 | if (state == Handshaking) { |
503 | // during handshake, only send from_net (no app data) |
504 | |
505 | if (mode == TLS::Stream) { |
506 | arg_from_net = from_net; |
507 | from_net.clear(); |
508 | } else { |
509 | // note: there may not be a packet |
510 | if (!packet_from_net.isEmpty()) |
511 | arg_from_net = packet_from_net.takeFirst(); |
512 | } |
513 | } else { |
514 | if (mode == TLS::Stream) { |
515 | if (!from_net.isEmpty()) { |
516 | arg_from_net = from_net; |
517 | from_net.clear(); |
518 | } |
519 | |
520 | if (!out.isEmpty()) { |
521 | out_pending += out.size(); |
522 | arg_from_app = out; |
523 | out.clear(); |
524 | } |
525 | } else { |
526 | if (!packet_from_net.isEmpty()) |
527 | arg_from_net = packet_from_net.takeFirst(); |
528 | |
529 | if (!packet_out.isEmpty()) { |
530 | arg_from_app = packet_out.takeFirst(); |
531 | ++packet_out_pending; |
532 | } |
533 | } |
534 | } |
535 | |
536 | if (arg_from_net.isEmpty() && arg_from_app.isEmpty() && !maybe_input) { |
537 | QCA_logTextMessage( |
538 | QStringLiteral("tls[%1]: ignoring update: no output and no expected input" ).arg(q->objectName()), |
539 | Logger::Information); |
540 | return; |
541 | } |
542 | |
543 | // clear this flag |
544 | maybe_input = false; |
545 | |
546 | QCA_logTextMessage(QStringLiteral("tls[%1]: c->update" ).arg(q->objectName()), Logger::Information); |
547 | op = OpUpdate; |
548 | c->update(from_net: arg_from_net, from_app: arg_from_app); |
549 | } |
550 | |
551 | void start_finished() |
552 | { |
553 | const bool ok = c->result() == TLSContext::Success; |
554 | if (!ok) { |
555 | reset(mode: ResetSession); |
556 | errorCode = TLS::ErrorInit; |
557 | emit q->error(); |
558 | return; |
559 | } |
560 | |
561 | state = Handshaking; |
562 | |
563 | // immediately update so we can get the first packet to send |
564 | maybe_input = true; |
565 | update(); |
566 | } |
567 | |
568 | void update_finished() |
569 | { |
570 | const TLSContext::Result r = c->result(); |
571 | if (r == TLSContext::Error) { |
572 | if (state == Handshaking || state == Closing) { |
573 | reset(mode: ResetSession); |
574 | errorCode = ErrorHandshake; |
575 | } else { |
576 | reset(mode: ResetSession); |
577 | errorCode = ErrorCrypt; |
578 | } |
579 | |
580 | emit q->error(); |
581 | return; |
582 | } |
583 | |
584 | const QByteArray c_to_net = c->to_net(); |
585 | if (!c_to_net.isEmpty()) { |
586 | QCA_logTextMessage( |
587 | QStringLiteral("tls[%1]: to_net %2" ).arg(q->objectName(), QString::number(c_to_net.size())), |
588 | Logger::Information); |
589 | } |
590 | |
591 | if (state == Closing) { |
592 | if (mode == TLS::Stream) |
593 | to_net += c_to_net; |
594 | else |
595 | packet_to_net += c_to_net; |
596 | |
597 | if (!c_to_net.isEmpty()) |
598 | actionQueue += Action(Action::ReadyReadOutgoing); |
599 | |
600 | if (r == TLSContext::Success) |
601 | actionQueue += Action(Action::Close); |
602 | |
603 | processNextAction(); |
604 | return; |
605 | } else if (state == Handshaking) { |
606 | if (mode == TLS::Stream) |
607 | to_net += c_to_net; |
608 | else |
609 | packet_to_net += c_to_net; |
610 | |
611 | if (!c_to_net.isEmpty()) |
612 | actionQueue += Action(Action::ReadyReadOutgoing); |
613 | |
614 | bool clientHello = false; |
615 | bool serverHello = false; |
616 | if (server) |
617 | clientHello = c->clientHelloReceived(); |
618 | else |
619 | serverHello = c->serverHelloReceived(); |
620 | |
621 | // client specifies a host? |
622 | if (!emitted_hostNameReceived && clientHello) { |
623 | host = c->hostName(); |
624 | if (!host.isEmpty()) |
625 | actionQueue += Action(Action::HostNameReceived); |
626 | } |
627 | |
628 | // successful handshake or server hello means there might be a peer cert |
629 | if (!emitted_peerCertificateAvailable && (r == TLSContext::Success || (!server && serverHello))) |
630 | actionQueue += Action(Action::CheckPeerCertificate); |
631 | |
632 | // server requests a cert from us? |
633 | if (!emitted_certificateRequested && (serverHello && c->certificateRequested())) |
634 | actionQueue += Action(Action::CertificateRequested); |
635 | |
636 | if (r == TLSContext::Success) { |
637 | sessionInfo = c->sessionInfo(); |
638 | if (sessionInfo.id) { |
639 | TLSSessionContext *sc = static_cast<TLSSessionContext *>(sessionInfo.id->clone()); |
640 | session.change(c: sc); |
641 | } |
642 | |
643 | actionQueue += Action(Action::Handshaken); |
644 | } |
645 | |
646 | processNextAction(); |
647 | return; |
648 | } else // Connected |
649 | { |
650 | const QByteArray c_to_app = c->to_app(); |
651 | if (!c_to_app.isEmpty()) { |
652 | QCA_logTextMessage( |
653 | QStringLiteral("tls[%1]: to_app %2" ).arg(q->objectName(), QString::number(c_to_app.size())), |
654 | Logger::Information); |
655 | } |
656 | |
657 | const bool eof = c->eof(); |
658 | int enc = -1; |
659 | if (!c_to_net.isEmpty()) |
660 | enc = c->encoded(); |
661 | |
662 | bool io_pending = false; |
663 | if (mode == TLS::Stream) { |
664 | if (!c_to_net.isEmpty()) |
665 | out_pending -= enc; |
666 | |
667 | if (out_pending > 0) { |
668 | maybe_input = true; |
669 | io_pending = true; |
670 | } |
671 | |
672 | if (!out.isEmpty()) |
673 | io_pending = true; |
674 | } else { |
675 | if (!c_to_net.isEmpty()) |
676 | --packet_out_pending; |
677 | |
678 | if (packet_out_pending > 0) { |
679 | maybe_input = true; |
680 | io_pending = true; |
681 | } |
682 | |
683 | if (!packet_out.isEmpty()) |
684 | io_pending = true; |
685 | } |
686 | |
687 | if (mode == TLS::Stream) { |
688 | to_net += c_to_net; |
689 | in += c_to_app; |
690 | to_net_encoded += enc; |
691 | } else { |
692 | packet_to_net += c_to_net; |
693 | packet_in += c_to_app; |
694 | } |
695 | |
696 | if (!c_to_net.isEmpty()) |
697 | actionQueue += Action(Action::ReadyReadOutgoing); |
698 | |
699 | if (!c_to_app.isEmpty()) |
700 | actionQueue += Action(Action::ReadyRead); |
701 | |
702 | if (eof) { |
703 | close(); |
704 | maybe_input = true; |
705 | } |
706 | |
707 | if (eof || io_pending) { |
708 | QCA_logTextMessage(QStringLiteral("tls[%1]: eof || io_pending" ).arg(q->objectName()), |
709 | Logger::Information); |
710 | update(); |
711 | } |
712 | |
713 | processNextAction(); |
714 | return; |
715 | } |
716 | } |
717 | |
718 | private Q_SLOTS: |
719 | void tls_resultsReady() |
720 | { |
721 | QCA_logTextMessage(QStringLiteral("tls[%1]: c->resultsReady()" ).arg(q->objectName()), Logger::Information); |
722 | |
723 | Q_ASSERT(op != -1); |
724 | |
725 | int last_op = op; |
726 | op = -1; |
727 | |
728 | if (last_op == OpStart) |
729 | start_finished(); |
730 | else // OpUpdate |
731 | update_finished(); |
732 | } |
733 | |
734 | void tls_dtlsTimeout() |
735 | { |
736 | QCA_logTextMessage(QStringLiteral("tls[%1]: c->dtlsTimeout()" ).arg(q->objectName()), Logger::Information); |
737 | |
738 | maybe_input = true; |
739 | update(); |
740 | } |
741 | |
742 | void doNextAction() |
743 | { |
744 | processNextAction(); |
745 | } |
746 | }; |
747 | |
748 | TLS::TLS(QObject *parent, const QString &provider) |
749 | : SecureLayer(parent) |
750 | , Algorithm(QStringLiteral("tls" ), provider) |
751 | { |
752 | d = new Private(this, TLS::Stream); |
753 | } |
754 | |
755 | TLS::TLS(Mode mode, QObject *parent, const QString &provider) |
756 | : SecureLayer(parent) |
757 | , Algorithm(mode == Stream ? QStringLiteral("tls" ) : QStringLiteral("dtls" ), provider) |
758 | { |
759 | d = new Private(this, mode); |
760 | } |
761 | |
762 | TLS::~TLS() |
763 | { |
764 | delete d; |
765 | } |
766 | |
767 | void TLS::reset() |
768 | { |
769 | d->reset(mode: ResetAll); |
770 | } |
771 | |
772 | QStringList TLS::supportedCipherSuites( |
773 | const Version &version) const // clazy:exclude=function-args-by-value TODO make it remove the & when we break ABI |
774 | { |
775 | return d->c->supportedCipherSuites(version); |
776 | } |
777 | |
778 | void TLS::setCertificate(const CertificateChain &cert, const PrivateKey &key) |
779 | { |
780 | d->localCert = cert; |
781 | d->localKey = key; |
782 | if (d->state != TLS::Private::Inactive) |
783 | d->c->setCertificate(cert, key); |
784 | } |
785 | |
786 | void TLS::setCertificate(const KeyBundle &kb) |
787 | { |
788 | setCertificate(cert: kb.certificateChain(), key: kb.privateKey()); |
789 | } |
790 | |
791 | CertificateCollection TLS::trustedCertificates() const |
792 | { |
793 | return d->trusted; |
794 | } |
795 | |
796 | void TLS::setTrustedCertificates(const CertificateCollection &trusted) |
797 | { |
798 | d->trusted = trusted; |
799 | if (d->state != TLS::Private::Inactive) |
800 | d->c->setTrustedCertificates(trusted); |
801 | } |
802 | |
803 | void TLS::setConstraints(SecurityLevel s) |
804 | { |
805 | int min = 128; |
806 | switch (s) { |
807 | case SL_None: |
808 | min = 0; |
809 | break; |
810 | case SL_Integrity: |
811 | min = 1; |
812 | break; |
813 | case SL_Export: |
814 | min = 40; |
815 | break; |
816 | case SL_Baseline: |
817 | min = 128; |
818 | break; |
819 | case SL_High: |
820 | min = 129; |
821 | break; |
822 | case SL_Highest: |
823 | min = qMax(a: 129, b: d->c->maxSSF()); |
824 | break; |
825 | } |
826 | |
827 | d->con_ssfMode = true; |
828 | d->con_minSSF = min; |
829 | d->con_maxSSF = -1; |
830 | |
831 | if (d->state != TLS::Private::Inactive) |
832 | d->c->setConstraints(minSSF: d->con_minSSF, maxSSF: d->con_maxSSF); |
833 | } |
834 | |
835 | void TLS::setConstraints(int minSSF, int maxSSF) |
836 | { |
837 | d->con_ssfMode = true; |
838 | d->con_minSSF = minSSF; |
839 | d->con_maxSSF = maxSSF; |
840 | |
841 | if (d->state != TLS::Private::Inactive) |
842 | d->c->setConstraints(minSSF: d->con_minSSF, maxSSF: d->con_maxSSF); |
843 | } |
844 | |
845 | void TLS::setConstraints(const QStringList &cipherSuiteList) |
846 | { |
847 | d->con_ssfMode = false; |
848 | d->con_cipherSuites = cipherSuiteList; |
849 | |
850 | if (d->state != TLS::Private::Inactive) |
851 | d->c->setConstraints(d->con_cipherSuites); |
852 | } |
853 | |
854 | QList<CertificateInfoOrdered> TLS::issuerList() const |
855 | { |
856 | return d->issuerList; |
857 | } |
858 | |
859 | void TLS::setIssuerList(const QList<CertificateInfoOrdered> &issuers) |
860 | { |
861 | d->issuerList = issuers; |
862 | if (d->state != TLS::Private::Inactive) |
863 | d->c->setIssuerList(issuers); |
864 | } |
865 | |
866 | void TLS::setSession(const TLSSession &session) |
867 | { |
868 | d->session = session; |
869 | } |
870 | |
871 | bool TLS::canCompress() const |
872 | { |
873 | return d->c->canCompress(); |
874 | } |
875 | |
876 | bool TLS::canSetHostName() const |
877 | { |
878 | return d->c->canSetHostName(); |
879 | } |
880 | |
881 | bool TLS::compressionEnabled() const |
882 | { |
883 | return d->tryCompress; |
884 | } |
885 | |
886 | void TLS::setCompressionEnabled(bool b) |
887 | { |
888 | d->tryCompress = b; |
889 | } |
890 | |
891 | void TLS::startClient(const QString &host) |
892 | { |
893 | d->reset(mode: ResetSessionAndData); |
894 | d->host = host; |
895 | d->issuerList.clear(); |
896 | |
897 | // client mode |
898 | d->start(serverMode: false); |
899 | } |
900 | |
901 | void TLS::startServer() |
902 | { |
903 | d->reset(mode: ResetSessionAndData); |
904 | |
905 | // server mode |
906 | d->start(serverMode: true); |
907 | } |
908 | |
909 | void TLS::continueAfterStep() |
910 | { |
911 | d->continueAfterStep(); |
912 | } |
913 | |
914 | bool TLS::isHandshaken() const |
915 | { |
916 | if (d->state == TLS::Private::Connected || d->state == TLS::Private::Closing) |
917 | return true; |
918 | else |
919 | return false; |
920 | } |
921 | |
922 | bool TLS::isCompressed() const |
923 | { |
924 | return d->sessionInfo.isCompressed; |
925 | } |
926 | |
927 | TLS::Version TLS::version() const |
928 | { |
929 | return d->sessionInfo.version; |
930 | } |
931 | |
932 | QString TLS::cipherSuite() const |
933 | { |
934 | return d->sessionInfo.cipherSuite; |
935 | } |
936 | |
937 | int TLS::cipherBits() const |
938 | { |
939 | return d->sessionInfo.cipherBits; |
940 | } |
941 | |
942 | int TLS::cipherMaxBits() const |
943 | { |
944 | return d->sessionInfo.cipherMaxBits; |
945 | } |
946 | |
947 | TLSSession TLS::session() const |
948 | { |
949 | return d->session; |
950 | } |
951 | |
952 | TLS::Error TLS::errorCode() const |
953 | { |
954 | return d->errorCode; |
955 | } |
956 | |
957 | TLS::IdentityResult TLS::peerIdentityResult() const |
958 | { |
959 | if (d->peerCert.isEmpty()) |
960 | return NoCertificate; |
961 | |
962 | if (d->peerValidity != ValidityGood) |
963 | return InvalidCertificate; |
964 | |
965 | if (d->hostMismatch) |
966 | return HostMismatch; |
967 | |
968 | return Valid; |
969 | } |
970 | |
971 | Validity TLS::peerCertificateValidity() const |
972 | { |
973 | return d->peerValidity; |
974 | } |
975 | |
976 | CertificateChain TLS::localCertificateChain() const |
977 | { |
978 | return d->localCert; |
979 | } |
980 | |
981 | PrivateKey TLS::localPrivateKey() const |
982 | { |
983 | return d->localKey; |
984 | } |
985 | |
986 | CertificateChain TLS::peerCertificateChain() const |
987 | { |
988 | return d->peerCert; |
989 | } |
990 | |
991 | bool TLS::isClosable() const |
992 | { |
993 | return true; |
994 | } |
995 | |
996 | int TLS::bytesAvailable() const |
997 | { |
998 | if (d->mode == Stream) |
999 | return d->in.size(); |
1000 | else |
1001 | return 0; |
1002 | } |
1003 | |
1004 | int TLS::bytesOutgoingAvailable() const |
1005 | { |
1006 | if (d->mode == Stream) |
1007 | return d->to_net.size(); |
1008 | else |
1009 | return 0; |
1010 | } |
1011 | |
1012 | void TLS::close() |
1013 | { |
1014 | d->close(); |
1015 | d->update(); |
1016 | } |
1017 | |
1018 | void TLS::write(const QByteArray &a) |
1019 | { |
1020 | if (d->mode == Stream) { |
1021 | d->out.append(a); |
1022 | d->layer.addPlain(plain: a.size()); |
1023 | } else |
1024 | d->packet_out.append(t: a); |
1025 | QCA_logTextMessage(QStringLiteral("tls[%1]: write" ).arg(objectName()), Logger::Information); |
1026 | d->update(); |
1027 | } |
1028 | |
1029 | QByteArray TLS::read() |
1030 | { |
1031 | if (d->mode == Stream) { |
1032 | const QByteArray a = d->in; |
1033 | d->in.clear(); |
1034 | return a; |
1035 | } else { |
1036 | if (!d->packet_in.isEmpty()) |
1037 | return d->packet_in.takeFirst(); |
1038 | else |
1039 | return QByteArray(); |
1040 | } |
1041 | } |
1042 | |
1043 | void TLS::writeIncoming(const QByteArray &a) |
1044 | { |
1045 | if (d->mode == Stream) |
1046 | d->from_net.append(a); |
1047 | else |
1048 | d->packet_from_net.append(t: a); |
1049 | QCA_logTextMessage(QStringLiteral("tls[%1]: writeIncoming %2" ).arg(objectName(), QString::number(a.size())), |
1050 | Logger::Information); |
1051 | d->update(); |
1052 | } |
1053 | |
1054 | QByteArray TLS::readOutgoing(int *plainBytes) |
1055 | { |
1056 | if (d->mode == Stream) { |
1057 | const QByteArray a = d->to_net; |
1058 | d->to_net.clear(); |
1059 | if (plainBytes) |
1060 | *plainBytes = d->to_net_encoded; |
1061 | d->layer.specifyEncoded(encoded: a.size(), plain: d->to_net_encoded); |
1062 | d->to_net_encoded = 0; |
1063 | return a; |
1064 | } else { |
1065 | if (!d->packet_to_net.isEmpty()) { |
1066 | const QByteArray a = d->packet_to_net.takeFirst(); |
1067 | const int x = d->packet_to_net_encoded.takeFirst(); |
1068 | if (plainBytes) |
1069 | *plainBytes = x; |
1070 | return a; |
1071 | } else { |
1072 | if (plainBytes) |
1073 | *plainBytes = 0; |
1074 | return QByteArray(); |
1075 | } |
1076 | } |
1077 | } |
1078 | |
1079 | QByteArray TLS::readUnprocessed() |
1080 | { |
1081 | if (d->mode == Stream) { |
1082 | const QByteArray a = d->unprocessed; |
1083 | d->unprocessed.clear(); |
1084 | return a; |
1085 | } else |
1086 | return QByteArray(); |
1087 | } |
1088 | |
1089 | int TLS::convertBytesWritten(qint64 bytes) |
1090 | { |
1091 | return d->layer.finished(encoded: bytes); |
1092 | } |
1093 | |
1094 | int TLS::packetsAvailable() const |
1095 | { |
1096 | return d->packet_in.count(); |
1097 | } |
1098 | |
1099 | int TLS::packetsOutgoingAvailable() const |
1100 | { |
1101 | return d->packet_to_net.count(); |
1102 | } |
1103 | |
1104 | int TLS::packetMTU() const |
1105 | { |
1106 | return d->packet_mtu; |
1107 | } |
1108 | |
1109 | void TLS::setPacketMTU(int size) const |
1110 | { |
1111 | d->packet_mtu = size; |
1112 | if (d->state != TLS::Private::Inactive) |
1113 | d->c->setMTU(size); |
1114 | } |
1115 | |
1116 | void TLS::connectNotify(const QMetaMethod &signal) |
1117 | { |
1118 | if (signal == QMetaMethod::fromSignal(signal: &TLS::hostNameReceived)) |
1119 | d->connect_hostNameReceived = true; |
1120 | else if (signal == QMetaMethod::fromSignal(signal: &TLS::certificateRequested)) |
1121 | d->connect_certificateRequested = true; |
1122 | else if (signal == QMetaMethod::fromSignal(signal: &TLS::peerCertificateAvailable)) |
1123 | d->connect_peerCertificateAvailable = true; |
1124 | else if (signal == QMetaMethod::fromSignal(signal: &TLS::handshaken)) |
1125 | d->connect_handshaken = true; |
1126 | } |
1127 | |
1128 | void TLS::disconnectNotify(const QMetaMethod &signal) |
1129 | { |
1130 | if (signal == QMetaMethod::fromSignal(signal: &TLS::hostNameReceived)) |
1131 | d->connect_hostNameReceived = false; |
1132 | else if (signal == QMetaMethod::fromSignal(signal: &TLS::certificateRequested)) |
1133 | d->connect_certificateRequested = false; |
1134 | else if (signal == QMetaMethod::fromSignal(signal: &TLS::peerCertificateAvailable)) |
1135 | d->connect_peerCertificateAvailable = false; |
1136 | else if (signal == QMetaMethod::fromSignal(signal: &TLS::handshaken)) |
1137 | d->connect_handshaken = false; |
1138 | } |
1139 | |
1140 | //---------------------------------------------------------------------------- |
1141 | // SASL::Params |
1142 | //---------------------------------------------------------------------------- |
1143 | class SASL::Params::Private |
1144 | { |
1145 | public: |
1146 | bool needUsername, canSendAuthzid, needPassword, canSendRealm; |
1147 | }; |
1148 | |
1149 | SASL::Params::Params() |
1150 | : d(new Private) |
1151 | { |
1152 | } |
1153 | |
1154 | SASL::Params::Params(bool user, bool authzid, bool pass, bool realm) |
1155 | : d(new Private) |
1156 | { |
1157 | d->needUsername = user; |
1158 | d->canSendAuthzid = authzid; |
1159 | d->needPassword = pass; |
1160 | d->canSendRealm = realm; |
1161 | } |
1162 | |
1163 | SASL::Params::Params(const SASL::Params &from) |
1164 | : d(new Private(*from.d)) |
1165 | { |
1166 | } |
1167 | |
1168 | SASL::Params::~Params() |
1169 | { |
1170 | delete d; |
1171 | } |
1172 | |
1173 | SASL::Params &SASL::Params::operator=(const SASL::Params &from) |
1174 | { |
1175 | *d = *from.d; |
1176 | return *this; |
1177 | } |
1178 | |
1179 | bool SASL::Params::needUsername() const |
1180 | { |
1181 | return d->needUsername; |
1182 | } |
1183 | |
1184 | bool SASL::Params::canSendAuthzid() const |
1185 | { |
1186 | return d->canSendAuthzid; |
1187 | } |
1188 | |
1189 | bool SASL::Params::needPassword() const |
1190 | { |
1191 | return d->needPassword; |
1192 | } |
1193 | |
1194 | bool SASL::Params::canSendRealm() const |
1195 | { |
1196 | return d->canSendRealm; |
1197 | } |
1198 | |
1199 | //---------------------------------------------------------------------------- |
1200 | // SASL |
1201 | //---------------------------------------------------------------------------- |
1202 | /* |
1203 | These don't map, but I don't think it matters much.. |
1204 | SASL_TRYAGAIN (-8) transient failure (e.g., weak key) |
1205 | SASL_BADMAC (-9) integrity check failed |
1206 | -- client only codes -- |
1207 | SASL_WRONGMECH (-11) mechanism doesn't support requested feature |
1208 | SASL_NEWSECRET (-12) new secret needed |
1209 | -- server only codes -- |
1210 | SASL_TRANS (-17) One time use of a plaintext password will |
1211 | enable requested mechanism for user |
1212 | SASL_PWLOCK (-21) password locked |
1213 | SASL_NOCHANGE (-22) requested change was not needed |
1214 | */ |
1215 | |
1216 | class SASL::Private : public QObject |
1217 | { |
1218 | Q_OBJECT |
1219 | public: |
1220 | enum |
1221 | { |
1222 | OpStart, |
1223 | OpServerFirstStep, |
1224 | OpNextStep, |
1225 | OpTryAgain, |
1226 | OpUpdate |
1227 | }; |
1228 | |
1229 | class Action |
1230 | { |
1231 | public: |
1232 | enum Type |
1233 | { |
1234 | ClientStarted, |
1235 | NextStep, |
1236 | Authenticated, |
1237 | ReadyRead, |
1238 | ReadyReadOutgoing |
1239 | }; |
1240 | |
1241 | int type; |
1242 | QByteArray stepData; |
1243 | bool haveInit; |
1244 | |
1245 | Action(int _type) |
1246 | : type(_type) |
1247 | { |
1248 | } |
1249 | |
1250 | Action(int _type, const QByteArray &_stepData) |
1251 | : type(_type) |
1252 | , stepData(_stepData) |
1253 | { |
1254 | } |
1255 | |
1256 | Action(int _type, bool _haveInit, const QByteArray &_stepData) |
1257 | : type(_type) |
1258 | , stepData(_stepData) |
1259 | , haveInit(_haveInit) |
1260 | { |
1261 | } |
1262 | }; |
1263 | |
1264 | SASL *q; |
1265 | SASLContext *c; |
1266 | |
1267 | // persistent settings (survives ResetSessionAndData) |
1268 | AuthFlags auth_flags; |
1269 | int ssfmin, ssfmax; |
1270 | QString ext_authid; |
1271 | int ext_ssf; |
1272 | bool localSet, remoteSet; |
1273 | SASLContext::HostPort local, remote; |
1274 | bool set_username, set_authzid, set_password, set_realm; |
1275 | QString username, authzid, realm; |
1276 | SecureArray password; |
1277 | |
1278 | // session |
1279 | bool server; |
1280 | QStringList mechlist; |
1281 | QString server_realm; |
1282 | bool allowClientSendFirst; |
1283 | bool disableServerSendLast; |
1284 | SafeTimer actionTrigger; |
1285 | int op; |
1286 | QList<Action> actionQueue; |
1287 | bool need_update; |
1288 | bool first; |
1289 | bool authed; |
1290 | |
1291 | // data (survives ResetSession) |
1292 | QString mech; // selected mech |
1293 | Error errorCode; |
1294 | |
1295 | // stream i/o |
1296 | QByteArray in, out; |
1297 | QByteArray to_net, from_net; |
1298 | int out_pending; |
1299 | int to_net_encoded; |
1300 | LayerTracker layer; |
1301 | |
1302 | Private(SASL *_q) |
1303 | : QObject(_q) |
1304 | , q(_q) |
1305 | , actionTrigger(this) |
1306 | { |
1307 | c = nullptr; |
1308 | set_username = false; |
1309 | set_authzid = false; |
1310 | set_password = false; |
1311 | set_realm = false; |
1312 | |
1313 | connect(sender: &actionTrigger, signal: &SafeTimer::timeout, context: this, slot: &Private::doNextAction); |
1314 | actionTrigger.setSingleShot(true); |
1315 | |
1316 | reset(mode: ResetAll); |
1317 | |
1318 | c = static_cast<SASLContext *>(q->context()); |
1319 | |
1320 | // parent the context to us, so that moveToThread works |
1321 | c->setParent(this); |
1322 | |
1323 | connect(sender: c, signal: &SASLContext::resultsReady, context: this, slot: &Private::sasl_resultsReady); |
1324 | } |
1325 | |
1326 | ~Private() override |
1327 | { |
1328 | // context is owned by Algorithm, unparent so we don't double-delete |
1329 | c->setParent(nullptr); |
1330 | } |
1331 | |
1332 | void reset(ResetMode mode) |
1333 | { |
1334 | if (c) |
1335 | c->reset(); |
1336 | |
1337 | server = false; |
1338 | mechlist.clear(); |
1339 | server_realm = QString(); |
1340 | allowClientSendFirst = false; |
1341 | disableServerSendLast = true; |
1342 | actionTrigger.stop(); |
1343 | op = -1; |
1344 | actionQueue.clear(); |
1345 | need_update = false; |
1346 | first = false; |
1347 | authed = false; |
1348 | |
1349 | out.clear(); |
1350 | out_pending = 0; |
1351 | |
1352 | if (mode >= ResetSessionAndData) { |
1353 | mech = QString(); |
1354 | errorCode = (SASL::Error)-1; |
1355 | |
1356 | in.clear(); |
1357 | to_net.clear(); |
1358 | from_net.clear(); |
1359 | to_net_encoded = 0; |
1360 | layer.reset(); |
1361 | } |
1362 | |
1363 | if (mode >= ResetAll) { |
1364 | auth_flags = SASL::AuthFlagsNone; |
1365 | ssfmin = 0; |
1366 | ssfmax = 0; |
1367 | ext_authid = QString(); |
1368 | ext_ssf = 0; |
1369 | localSet = false; |
1370 | remoteSet = false; |
1371 | local = SASLContext::HostPort(); |
1372 | remote = SASLContext::HostPort(); |
1373 | |
1374 | set_username = false; |
1375 | username = QString(); |
1376 | set_authzid = false; |
1377 | authzid = QString(); |
1378 | set_password = false; |
1379 | password = SecureArray(); |
1380 | set_realm = false; |
1381 | realm = QString(); |
1382 | } |
1383 | } |
1384 | |
1385 | void setup(const QString &service, const QString &host) |
1386 | { |
1387 | c->setup(service, host, local: localSet ? &local : nullptr, remote: remoteSet ? &remote : nullptr, ext_id: ext_authid, ext_ssf); |
1388 | c->setConstraints(f: auth_flags, minSSF: ssfmin, maxSSF: ssfmax); |
1389 | |
1390 | QString *p_username = nullptr; |
1391 | QString *p_authzid = nullptr; |
1392 | SecureArray *p_password = nullptr; |
1393 | QString *p_realm = nullptr; |
1394 | |
1395 | if (set_username) |
1396 | p_username = &username; |
1397 | if (set_authzid) |
1398 | p_authzid = &authzid; |
1399 | if (set_password) |
1400 | p_password = &password; |
1401 | if (set_realm) |
1402 | p_realm = &realm; |
1403 | |
1404 | c->setClientParams(user: p_username, authzid: p_authzid, pass: p_password, realm: p_realm); |
1405 | } |
1406 | |
1407 | void start() |
1408 | { |
1409 | op = OpStart; |
1410 | first = true; |
1411 | |
1412 | if (server) { |
1413 | QCA_logTextMessage(QStringLiteral("sasl[%1]: c->startServer()" ).arg(q->objectName()), Logger::Information); |
1414 | c->startServer(realm: server_realm, disableServerSendLast); |
1415 | } else { |
1416 | QCA_logTextMessage(QStringLiteral("sasl[%1]: c->startClient()" ).arg(q->objectName()), Logger::Information); |
1417 | c->startClient(mechlist, allowClientSendFirst); |
1418 | } |
1419 | } |
1420 | |
1421 | void putServerFirstStep(const QString &mech, const QByteArray *clientInit) |
1422 | { |
1423 | if (op != -1) |
1424 | return; |
1425 | |
1426 | QCA_logTextMessage(QStringLiteral("sasl[%1]: c->serverFirstStep()" ).arg(q->objectName()), Logger::Information); |
1427 | op = OpServerFirstStep; |
1428 | c->serverFirstStep(mech, clientInit); |
1429 | } |
1430 | |
1431 | void putStep(const QByteArray &stepData) |
1432 | { |
1433 | if (op != -1) |
1434 | return; |
1435 | |
1436 | QCA_logTextMessage(QStringLiteral("sasl[%1]: c->nextStep()" ).arg(q->objectName()), Logger::Information); |
1437 | op = OpNextStep; |
1438 | c->nextStep(from_net: stepData); |
1439 | } |
1440 | |
1441 | void tryAgain() |
1442 | { |
1443 | if (op != -1) |
1444 | return; |
1445 | |
1446 | QCA_logTextMessage(QStringLiteral("sasl[%1]: c->tryAgain()" ).arg(q->objectName()), Logger::Information); |
1447 | op = OpTryAgain; |
1448 | c->tryAgain(); |
1449 | } |
1450 | |
1451 | void processNextAction() |
1452 | { |
1453 | if (actionQueue.isEmpty()) { |
1454 | if (need_update) |
1455 | update(); |
1456 | return; |
1457 | } |
1458 | |
1459 | const Action a = actionQueue.takeFirst(); |
1460 | |
1461 | // set up for the next one, if necessary |
1462 | if (!actionQueue.isEmpty() || need_update) { |
1463 | if (!actionTrigger.isActive()) |
1464 | actionTrigger.start(); |
1465 | } |
1466 | |
1467 | if (a.type == Action::ClientStarted) { |
1468 | emit q->clientStarted(clientInit: a.haveInit, clientInitData: a.stepData); |
1469 | } else if (a.type == Action::NextStep) { |
1470 | emit q->nextStep(stepData: a.stepData); |
1471 | } else if (a.type == Action::Authenticated) { |
1472 | authed = true; |
1473 | |
1474 | // write any app data waiting during authentication |
1475 | if (!out.isEmpty()) { |
1476 | need_update = true; |
1477 | if (!actionTrigger.isActive()) |
1478 | actionTrigger.start(); |
1479 | } |
1480 | |
1481 | QCA_logTextMessage(QStringLiteral("sasl[%1]: authenticated" ).arg(q->objectName()), Logger::Information); |
1482 | emit q->authenticated(); |
1483 | } else if (a.type == Action::ReadyRead) { |
1484 | emit q->readyRead(); |
1485 | } else if (a.type == Action::ReadyReadOutgoing) { |
1486 | emit q->readyReadOutgoing(); |
1487 | } |
1488 | } |
1489 | |
1490 | void update() |
1491 | { |
1492 | // defer writes while authenticating |
1493 | if (!authed) { |
1494 | QCA_logTextMessage( |
1495 | QStringLiteral("sasl[%1]: ignoring update while not yet authenticated" ).arg(q->objectName()), |
1496 | Logger::Information); |
1497 | return; |
1498 | } |
1499 | |
1500 | if (!actionQueue.isEmpty()) { |
1501 | QCA_logTextMessage( |
1502 | QStringLiteral("sasl[%1]: ignoring update while processing actions" ).arg(q->objectName()), |
1503 | Logger::Information); |
1504 | need_update = true; |
1505 | return; |
1506 | } |
1507 | |
1508 | // only allow one operation at a time |
1509 | if (op != -1) { |
1510 | QCA_logTextMessage(QStringLiteral("sasl[%1]: ignoring update while operation active" ).arg(q->objectName()), |
1511 | Logger::Information); |
1512 | need_update = true; |
1513 | return; |
1514 | } |
1515 | |
1516 | need_update = false; |
1517 | |
1518 | QCA_logTextMessage(QStringLiteral("sasl[%1]: c->update()" ).arg(q->objectName()), Logger::Information); |
1519 | op = OpUpdate; |
1520 | out_pending += out.size(); |
1521 | c->update(from_net, from_app: out); |
1522 | from_net.clear(); |
1523 | out.clear(); |
1524 | } |
1525 | |
1526 | private Q_SLOTS: |
1527 | void sasl_resultsReady() |
1528 | { |
1529 | QCA_logTextMessage(QStringLiteral("sasl[%1]: c->resultsReady()" ).arg(q->objectName()), Logger::Information); |
1530 | |
1531 | int last_op = op; |
1532 | op = -1; |
1533 | |
1534 | const SASLContext::Result r = c->result(); |
1535 | |
1536 | if (last_op == OpStart) { |
1537 | if (server) { |
1538 | if (r != SASLContext::Success) { |
1539 | errorCode = SASL::ErrorInit; |
1540 | emit q->error(); |
1541 | return; |
1542 | } |
1543 | |
1544 | emit q->serverStarted(); |
1545 | return; |
1546 | } else // client |
1547 | { |
1548 | mech = c->mech(); |
1549 | |
1550 | // fall into this logic |
1551 | last_op = OpTryAgain; |
1552 | } |
1553 | } else if (last_op == OpServerFirstStep) { |
1554 | // fall into this logic |
1555 | last_op = OpTryAgain; |
1556 | } else if (last_op == OpNextStep) { |
1557 | // fall into this logic |
1558 | last_op = OpTryAgain; |
1559 | } |
1560 | |
1561 | if (last_op == OpTryAgain) { |
1562 | if (server) { |
1563 | if (r == SASLContext::Continue) { |
1564 | emit q->nextStep(stepData: c->stepData()); |
1565 | return; |
1566 | } else if (r == SASLContext::AuthCheck) { |
1567 | emit q->authCheck(user: c->username(), authzid: c->authzid()); |
1568 | return; |
1569 | } else if (r == SASLContext::Success) { |
1570 | if (!disableServerSendLast) |
1571 | actionQueue += Action(Action::NextStep, c->stepData()); |
1572 | |
1573 | actionQueue += Action(Action::Authenticated); |
1574 | |
1575 | processNextAction(); |
1576 | return; |
1577 | } else // error |
1578 | { |
1579 | errorCode = SASL::ErrorHandshake; |
1580 | emit q->error(); |
1581 | return; |
1582 | } |
1583 | } else // client |
1584 | { |
1585 | if (first) { |
1586 | if (r == SASLContext::Error) { |
1587 | if (first) |
1588 | errorCode = SASL::ErrorInit; |
1589 | else |
1590 | errorCode = SASL::ErrorHandshake; |
1591 | emit q->error(); |
1592 | return; |
1593 | } else if (r == SASLContext::Params) { |
1594 | const Params np = c->clientParams(); |
1595 | emit q->needParams(params: np); |
1596 | return; |
1597 | } |
1598 | |
1599 | first = false; |
1600 | actionQueue += Action(Action::ClientStarted, c->haveClientInit(), c->stepData()); |
1601 | if (r == SASLContext::Success) |
1602 | actionQueue += Action(Action::Authenticated); |
1603 | |
1604 | processNextAction(); |
1605 | return; |
1606 | } else { |
1607 | if (r == SASLContext::Error) { |
1608 | errorCode = ErrorHandshake; |
1609 | emit q->error(); |
1610 | return; |
1611 | } else if (r == SASLContext::Params) { |
1612 | const Params np = c->clientParams(); |
1613 | emit q->needParams(params: np); |
1614 | return; |
1615 | } else if (r == SASLContext::Continue) { |
1616 | emit q->nextStep(stepData: c->stepData()); |
1617 | return; |
1618 | } else if (r == SASLContext::Success) { |
1619 | actionQueue += Action(Action::NextStep, c->stepData()); |
1620 | actionQueue += Action(Action::Authenticated); |
1621 | |
1622 | processNextAction(); |
1623 | return; |
1624 | } |
1625 | } |
1626 | } |
1627 | } else if (last_op == OpUpdate) { |
1628 | if (r != SASLContext::Success) { |
1629 | errorCode = ErrorCrypt; |
1630 | emit q->error(); |
1631 | return; |
1632 | } |
1633 | |
1634 | const QByteArray c_to_net = c->to_net(); |
1635 | const QByteArray c_to_app = c->to_app(); |
1636 | int enc = -1; |
1637 | if (!c_to_net.isEmpty()) |
1638 | enc = c->encoded(); |
1639 | |
1640 | bool io_pending = false; |
1641 | if (!c_to_net.isEmpty()) |
1642 | out_pending -= enc; |
1643 | |
1644 | if (out_pending > 0) |
1645 | io_pending = true; |
1646 | |
1647 | if (!out.isEmpty()) |
1648 | io_pending = true; |
1649 | |
1650 | to_net += c_to_net; |
1651 | in += c_to_app; |
1652 | to_net_encoded += enc; |
1653 | |
1654 | if (!c_to_net.isEmpty()) |
1655 | actionQueue += Action(Action::ReadyReadOutgoing); |
1656 | |
1657 | if (!c_to_app.isEmpty()) |
1658 | actionQueue += Action(Action::ReadyRead); |
1659 | |
1660 | if (io_pending) |
1661 | update(); |
1662 | |
1663 | processNextAction(); |
1664 | return; |
1665 | } |
1666 | } |
1667 | |
1668 | void doNextAction() |
1669 | { |
1670 | processNextAction(); |
1671 | } |
1672 | }; |
1673 | |
1674 | SASL::SASL(QObject *parent, const QString &provider) |
1675 | : SecureLayer(parent) |
1676 | , Algorithm(QStringLiteral("sasl" ), provider) |
1677 | { |
1678 | d = new Private(this); |
1679 | } |
1680 | |
1681 | SASL::~SASL() |
1682 | { |
1683 | delete d; |
1684 | } |
1685 | |
1686 | void SASL::reset() |
1687 | { |
1688 | d->reset(mode: ResetAll); |
1689 | } |
1690 | |
1691 | SASL::Error SASL::errorCode() const |
1692 | { |
1693 | return d->errorCode; |
1694 | } |
1695 | |
1696 | SASL::AuthCondition SASL::authCondition() const |
1697 | { |
1698 | return d->c->authCondition(); |
1699 | } |
1700 | |
1701 | void SASL::setConstraints(AuthFlags f, SecurityLevel s) |
1702 | { |
1703 | int min = 0; |
1704 | if (s == SL_Integrity) |
1705 | min = 1; |
1706 | else if (s == SL_Export) |
1707 | min = 56; |
1708 | else if (s == SL_Baseline) |
1709 | min = 128; |
1710 | else if (s == SL_High) |
1711 | min = 192; |
1712 | else if (s == SL_Highest) |
1713 | min = 256; |
1714 | |
1715 | setConstraints(f, minSSF: min, maxSSF: 256); |
1716 | } |
1717 | |
1718 | void SASL::setConstraints(AuthFlags f, int minSSF, int maxSSF) |
1719 | { |
1720 | d->auth_flags = f; |
1721 | |
1722 | d->ssfmin = minSSF; |
1723 | d->ssfmax = maxSSF; |
1724 | } |
1725 | |
1726 | void SASL::setExternalAuthId(const QString &authid) |
1727 | { |
1728 | d->ext_authid = authid; |
1729 | } |
1730 | |
1731 | void SASL::setExternalSSF(int strength) |
1732 | { |
1733 | d->ext_ssf = strength; |
1734 | } |
1735 | |
1736 | void SASL::setLocalAddress(const QString &addr, quint16 port) |
1737 | { |
1738 | d->localSet = true; |
1739 | d->local.addr = addr; |
1740 | d->local.port = port; |
1741 | } |
1742 | |
1743 | void SASL::setRemoteAddress(const QString &addr, quint16 port) |
1744 | { |
1745 | d->remoteSet = true; |
1746 | d->remote.addr = addr; |
1747 | d->remote.port = port; |
1748 | } |
1749 | |
1750 | void SASL::startClient(const QString &service, const QString &host, const QStringList &mechlist, ClientSendMode mode) |
1751 | { |
1752 | d->reset(mode: ResetSessionAndData); |
1753 | d->setup(service, host); |
1754 | d->server = false; |
1755 | d->mechlist = mechlist; |
1756 | d->allowClientSendFirst = (mode == AllowClientSendFirst); |
1757 | d->start(); |
1758 | } |
1759 | |
1760 | void SASL::startServer(const QString &service, const QString &host, const QString &realm, ServerSendMode mode) |
1761 | { |
1762 | d->reset(mode: ResetSessionAndData); |
1763 | d->setup(service, host); |
1764 | d->server = true; |
1765 | d->server_realm = realm; |
1766 | d->disableServerSendLast = (mode == DisableServerSendLast); |
1767 | d->start(); |
1768 | } |
1769 | |
1770 | void SASL::putServerFirstStep(const QString &mech) |
1771 | { |
1772 | d->putServerFirstStep(mech, clientInit: nullptr); |
1773 | } |
1774 | |
1775 | void SASL::putServerFirstStep(const QString &mech, const QByteArray &clientInit) |
1776 | { |
1777 | d->putServerFirstStep(mech, clientInit: &clientInit); |
1778 | } |
1779 | |
1780 | void SASL::putStep(const QByteArray &stepData) |
1781 | { |
1782 | d->putStep(stepData); |
1783 | } |
1784 | |
1785 | void SASL::setUsername(const QString &user) |
1786 | { |
1787 | d->set_username = true; |
1788 | d->username = user; |
1789 | d->c->setClientParams(user: &user, authzid: nullptr, pass: nullptr, realm: nullptr); |
1790 | } |
1791 | |
1792 | void SASL::setAuthzid(const QString &authzid) |
1793 | { |
1794 | d->set_authzid = true; |
1795 | d->authzid = authzid; |
1796 | d->c->setClientParams(user: nullptr, authzid: &authzid, pass: nullptr, realm: nullptr); |
1797 | } |
1798 | |
1799 | void SASL::setPassword(const SecureArray &pass) |
1800 | { |
1801 | d->set_password = true; |
1802 | d->password = pass; |
1803 | d->c->setClientParams(user: nullptr, authzid: nullptr, pass: &pass, realm: nullptr); |
1804 | } |
1805 | |
1806 | void SASL::setRealm(const QString &realm) |
1807 | { |
1808 | d->set_realm = true; |
1809 | d->realm = realm; |
1810 | d->c->setClientParams(user: nullptr, authzid: nullptr, pass: nullptr, realm: &realm); |
1811 | } |
1812 | |
1813 | void SASL::continueAfterParams() |
1814 | { |
1815 | d->tryAgain(); |
1816 | } |
1817 | |
1818 | void SASL::continueAfterAuthCheck() |
1819 | { |
1820 | d->tryAgain(); |
1821 | } |
1822 | |
1823 | QString SASL::mechanism() const |
1824 | { |
1825 | return d->mech; |
1826 | } |
1827 | |
1828 | QStringList SASL::mechanismList() const |
1829 | { |
1830 | return d->c->mechlist(); |
1831 | } |
1832 | |
1833 | QStringList SASL::realmList() const |
1834 | { |
1835 | return d->c->realmlist(); |
1836 | } |
1837 | |
1838 | int SASL::ssf() const |
1839 | { |
1840 | return d->c->ssf(); |
1841 | } |
1842 | |
1843 | int SASL::bytesAvailable() const |
1844 | { |
1845 | return d->in.size(); |
1846 | } |
1847 | |
1848 | int SASL::bytesOutgoingAvailable() const |
1849 | { |
1850 | return d->to_net.size(); |
1851 | } |
1852 | |
1853 | void SASL::write(const QByteArray &a) |
1854 | { |
1855 | d->out.append(a); |
1856 | d->layer.addPlain(plain: a.size()); |
1857 | d->update(); |
1858 | } |
1859 | |
1860 | QByteArray SASL::read() |
1861 | { |
1862 | const QByteArray a = d->in; |
1863 | d->in.clear(); |
1864 | return a; |
1865 | } |
1866 | |
1867 | void SASL::writeIncoming(const QByteArray &a) |
1868 | { |
1869 | d->from_net.append(a); |
1870 | d->update(); |
1871 | } |
1872 | |
1873 | QByteArray SASL::readOutgoing(int *plainBytes) |
1874 | { |
1875 | const QByteArray a = d->to_net; |
1876 | d->to_net.clear(); |
1877 | if (plainBytes) |
1878 | *plainBytes = d->to_net_encoded; |
1879 | d->layer.specifyEncoded(encoded: a.size(), plain: d->to_net_encoded); |
1880 | d->to_net_encoded = 0; |
1881 | return a; |
1882 | } |
1883 | |
1884 | int SASL::convertBytesWritten(qint64 bytes) |
1885 | { |
1886 | return d->layer.finished(encoded: bytes); |
1887 | } |
1888 | |
1889 | } |
1890 | |
1891 | #include "qca_securelayer.moc" |
1892 | |