]> git.saurik.com Git - apple/security.git/blob - keychain/SecureObjectSync/SOSCoder.c
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / SecureObjectSync / SOSCoder.c
1 /*
2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <stdlib.h>
25
26 #include <CoreFoundation/CFBase.h>
27 #include <CoreFoundation/CFError.h>
28
29 #include <Security/SecBasePriv.h>
30 #include <Security/SecOTR.h>
31 #include <Security/SecOTRSession.h>
32 #include <Security/SecOTRSessionPriv.h>
33 #include "keychain/SecureObjectSync/SOSInternal.h"
34 #include "keychain/SecureObjectSync/SOSFullPeerInfo.h"
35 #include <Security/SecureObjectSync/SOSPeerInfo.h>
36 #include "keychain/SecureObjectSync/SOSPeer.h"
37 #include "keychain/SecureObjectSync/SOSCoder.h"
38
39 #include <utilities/SecCFRelease.h>
40 #include <utilities/SecCFWrappers.h>
41 #include <utilities/SecIOFormat.h>
42 #include <utilities/SecCFError.h>
43 #include <utilities/SecCoreCrypto.h>
44 #include <utilities/debugging.h>
45
46 #include <utilities/der_plist.h>
47 #include <utilities/der_plist_internal.h>
48
49 #include <corecrypto/ccder.h>
50 #include <utilities/iCloudKeychainTrace.h>
51 #include <utilities/SecADWrapper.h>
52
53 #include "AssertMacros.h"
54
55 struct __OpaqueSOSCoder {
56 CFRuntimeBase _base;
57
58 CFStringRef peer_id;
59 SecOTRSessionRef sessRef;
60 bool waitingForDataPacket;
61 CFDataRef pendingResponse;
62
63 CFDataRef hashOfLastReceived;
64 bool lastReceivedWasOld;
65 };
66
67 #define lastReceived_di ccsha1_di
68
69 CFGiblisWithCompareFor(SOSCoder)
70
71 static CFStringRef SOSCoderCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
72 SOSCoderRef coder = (SOSCoderRef)cf;
73 if(coder){
74 __block CFStringRef desc = NULL;
75 CFDataPerformWithHexString(coder->hashOfLastReceived, ^(CFStringRef dataString) {
76 desc = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<Coder %@ %@ %s%s>"),
77 coder->sessRef,
78 dataString,
79 coder->waitingForDataPacket ? "W" : "w",
80 coder->lastReceivedWasOld ? "O" : "o"
81 );
82
83 });
84 return desc;
85 }
86 else
87 return CFSTR("NULL");
88 }
89
90 static Boolean SOSCoderCompare(CFTypeRef cfA, CFTypeRef cfB) {
91 SOSCoderRef coderA = (SOSCoderRef)cfA, coderB = (SOSCoderRef)cfB;
92 // Use mainly to see if peerB is actually this device (peerA)
93 return CFStringCompare(coderA->peer_id, coderB->peer_id, 0) == kCFCompareEqualTo;
94 }
95
96
97 static const char *SOSCoderString(SOSCoderStatus coderStatus) {
98 switch (coderStatus) {
99 case kSOSCoderDataReturned: return "DataReturned";
100 case kSOSCoderNegotiating: return "Negotiating";
101 case kSOSCoderNegotiationCompleted: return "NegotiationCompleted";
102 case kSOSCoderFailure: return "Failure";
103 case kSOSCoderStaleEvent: return "StaleEvent";
104 case kSOSCoderTooNew: return "TooNew";
105 default: return "StatusUnknown";
106 }
107 }
108
109 static CFMutableDataRef sessSerializedCreate(SOSCoderRef coder, CFErrorRef *error) {
110 CFMutableDataRef otr_state = NULL;
111
112 if(!coder || !coder->sessRef) {
113 SOSCreateErrorWithFormat(kSOSErrorUnexpectedType, NULL, error, 0, CFSTR("No session reference."));
114 return NULL;
115 }
116
117 if ((otr_state = CFDataCreateMutable(NULL, 0)) == NULL) {
118 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, NULL, error, 0, CFSTR("Mutable Data allocation failed."));
119 return NULL;
120 }
121
122 if (errSecSuccess != SecOTRSAppendSerialization(coder->sessRef, otr_state)) {
123 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, NULL, error, 0, CFSTR("Append Serialization failed."));
124 CFReleaseSafe(otr_state);
125 return NULL;
126 }
127
128 return otr_state;
129
130 }
131
132 static size_t der_sizeof_optional_data(CFDataRef data) {
133 return data ? der_sizeof_data(data, NULL) : 0;
134 }
135
136 static uint8_t* der_encode_optional_data(CFDataRef data, CFErrorRef *error, const uint8_t* der, uint8_t* der_end) {
137 return data ? der_encode_data(data, error, der, der_end) : der_end;
138 }
139
140
141
142 static size_t SOSCoderGetDEREncodedSize(SOSCoderRef coder, CFErrorRef *error) {
143 size_t encoded_size = 0;
144 CFMutableDataRef otr_state = sessSerializedCreate(coder, error);
145
146 if (otr_state) {
147 size_t data_size = der_sizeof_data(otr_state, error);
148 size_t waiting_size = ccder_sizeof_bool(coder->waitingForDataPacket, error);
149 size_t pending_size = der_sizeof_optional_data(coder->pendingResponse);
150
151 if ((data_size != 0) && (waiting_size != 0))
152 {
153 encoded_size = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, data_size + waiting_size + pending_size);
154 }
155 CFReleaseSafe(otr_state);
156 }
157 return encoded_size;
158 }
159
160
161 static uint8_t* SOSCoderEncodeToDER(SOSCoderRef coder, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
162 if(!der_end) return NULL;
163 uint8_t* result = NULL;
164 CFMutableDataRef otr_state = sessSerializedCreate(coder, error);
165
166 if(otr_state) {
167 result = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
168 der_encode_data(otr_state, error, der,
169 ccder_encode_bool(coder->waitingForDataPacket, der,
170 der_encode_optional_data(coder->pendingResponse, error, der, der_end))));
171 CFReleaseSafe(otr_state);
172 }
173 return result;
174 }
175
176 bool SOSCoderIsCoderInAwaitingState(SOSCoderRef coder){
177 return SecOTRSessionIsSessionInAwaitingState(coder->sessRef);
178 }
179
180 CFDataRef SOSCoderCopyDER(SOSCoderRef coder, CFErrorRef* error) {
181 CFMutableDataRef encoded = NULL;
182 size_t encoded_size = SOSCoderGetDEREncodedSize(coder, error);
183
184 if (encoded_size > 0) {
185 encoded = CFDataCreateMutable(NULL, encoded_size);
186 if (encoded) {
187 CFDataSetLength(encoded, encoded_size);
188 uint8_t * der = CFDataGetMutableBytePtr(encoded);
189 uint8_t * der_end = der + encoded_size;
190 if (!SOSCoderEncodeToDER(coder, error, der, der_end)) {
191 CFReleaseNull(encoded);
192 encoded = NULL;
193 }
194 }
195 }
196 return encoded;
197 }
198
199 static SOSCoderRef SOSCoderCreate_internal() {
200 SOSCoderRef p = CFTypeAllocate(SOSCoder, struct __OpaqueSOSCoder, kCFAllocatorDefault);
201
202 p->peer_id = NULL;
203 p->sessRef = NULL;
204 p->pendingResponse = NULL;
205 p->waitingForDataPacket = false;
206
207 p->hashOfLastReceived = NULL;
208 p->lastReceivedWasOld = false;
209
210 return p;
211
212 }
213
214 // 0 - Type not understood
215 // 1 - OCTET_STRING, just stored the data for OTR
216 // 2 - SEQUENCE with no version value
217 // 3 - SEQUENCE with version value we pull out of the CCDER_INTEGER
218
219 typedef enum coderExportFormatVersion {
220 kNotUnderstood = 0,
221 kCoderAsOTRDataOnly = 1,
222 kCoderAsSequence = 2,
223 kCoderAsVersionedSequence = 3,
224
225 kCurrentCoderExportVersion = kCoderAsVersionedSequence
226 } CoderExportFormatVersion;
227
228 static uint64_t SOSCoderGetExportedVersion(const uint8_t *der, const uint8_t *der_end) {
229 ccder_tag tag;
230 uint64_t result = kNotUnderstood;
231 require(ccder_decode_tag(&tag, der, der_end),xit);
232 switch (tag) {
233 case CCDER_OCTET_STRING: // TODO: this code is safe to delete?
234 result = kCoderAsOTRDataOnly;
235 break;
236
237 case CCDER_CONSTRUCTED_SEQUENCE:
238 {
239 const uint8_t *sequence_end = NULL;
240 der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
241 ccder_tag firstSequenceTag;
242 require(ccder_decode_tag(&firstSequenceTag, der, der_end),xit);
243
244 switch (firstSequenceTag) {
245 case CCDER_OCTET_STRING:
246 result = kCoderAsSequence;
247 break;
248 case CCDER_INTEGER:
249 der = ccder_decode_uint64(NULL, der, sequence_end);
250 if (der == NULL) {
251 result = kNotUnderstood;
252 } else {
253 result = kCoderAsVersionedSequence;
254 }
255 break;
256 }
257 }
258 }
259 xit:
260 return result;
261
262 }
263
264 SOSCoderRef SOSCoderCreateFromData(CFDataRef exportedData, CFErrorRef *error) {
265 // TODO: fill in errors for all failure cases
266 //require_action_quiet(coder, xit, SOSCreateError(kSOSErrorSendFailure, CFSTR("No coder for peer"), NULL, error));
267
268 SOSCoderRef p = SOSCoderCreate_internal();
269
270 const uint8_t *der = CFDataGetBytePtr(exportedData);
271 const uint8_t *der_end = der + CFDataGetLength(exportedData);
272
273 CFDataRef otr_data = NULL;
274
275 switch (SOSCoderGetExportedVersion(der, der_end)) {
276 case kCoderAsOTRDataOnly:
277 der = der_decode_data(kCFAllocatorDefault, 0, &otr_data, error, der, der_end);
278 p->waitingForDataPacket = false;
279 break;
280
281 case kCoderAsSequence:
282 {
283 const uint8_t *sequence_end = NULL;
284 der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
285
286 require_action_quiet(sequence_end == der_end, fail, SecCFDERCreateError(kSOSErrorDecodeFailure, CFSTR("Extra data in SOS coder"), NULL, error));
287
288 der = der_decode_data(kCFAllocatorDefault, 0, &otr_data, error, der, sequence_end);
289 der = ccder_decode_bool(&p->waitingForDataPacket, der, sequence_end);
290 if (der != sequence_end) { // optionally a pending response
291 der = der_decode_data(kCFAllocatorDefault, 0, &p->pendingResponse, error, der, sequence_end);
292 }
293 }
294 break;
295
296 case kCoderAsVersionedSequence:
297 {
298 const uint8_t *sequence_end = NULL;
299 der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
300
301 require_action_quiet(sequence_end == der_end, fail, SecCFDERCreateError(kSOSErrorDecodeFailure, CFSTR("Extra data in SOS coder"), NULL, error));
302
303 uint64_t version;
304 der = ccder_decode_uint64(&version, der, sequence_end);
305 if (version != kCoderAsVersionedSequence) {
306 SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unsupported Sequence Version: %lld"), version);
307 goto fail;
308 }
309
310 der = der_decode_data(kCFAllocatorDefault, 0, &otr_data, error, der, sequence_end);
311 der = ccder_decode_bool(&p->waitingForDataPacket, der, sequence_end);
312 der = ccder_decode_bool(&p->lastReceivedWasOld, der, sequence_end);
313 der = der_decode_data(kCFAllocatorDefault, 0, &p->hashOfLastReceived, error, der, sequence_end);
314 if (der != sequence_end) { // optionally a pending response
315 der = der_decode_data(kCFAllocatorDefault, 0, &p->pendingResponse, error, der, sequence_end);
316 }
317 }
318 break;
319
320 default:
321 SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("Unsupported SOS Coder DER"));
322 goto fail;
323 }
324
325 require(der, fail);
326
327 p->sessRef = SecOTRSessionCreateFromData(NULL, otr_data);
328 require(p->sessRef, fail);
329
330 if (p->hashOfLastReceived == NULL)
331 p->hashOfLastReceived = CFDataCreateMutableWithScratch(kCFAllocatorDefault, lastReceived_di()->output_size);
332
333 CFReleaseSafe(otr_data);
334 return p;
335
336 fail:
337 CFReleaseNull(p);
338 CFReleaseSafe(otr_data);
339 return NULL;
340 }
341
342
343 SOSCoderRef SOSCoderCreate(SOSPeerInfoRef peerInfo, SOSFullPeerInfoRef myPeerInfo, CFBooleanRef useCompact, CFErrorRef *error) {
344 CFAllocatorRef allocator = CFGetAllocator(peerInfo);
345
346 SOSCoderRef coder = SOSCoderCreate_internal();
347
348 CFErrorRef localError = NULL;
349
350 SecOTRFullIdentityRef myRef = NULL;
351 SecOTRPublicIdentityRef peerRef = NULL;
352 SecKeyRef privateKey = NULL;
353 SecKeyRef publicKey = NULL;
354
355 if (myPeerInfo && peerInfo) {
356 privateKey = SOSFullPeerInfoCopyDeviceKey(myPeerInfo, &localError);
357 require_quiet(privateKey, errOut);
358
359 myRef = SecOTRFullIdentityCreateFromSecKeyRef(allocator, privateKey, &localError);
360 require_quiet(myRef, errOut);
361
362 CFReleaseNull(privateKey);
363
364 publicKey = SOSPeerInfoCopyPubKey(peerInfo, &localError);
365 require(publicKey, errOut);
366
367 peerRef = SecOTRPublicIdentityCreateFromSecKeyRef(allocator, publicKey, &localError);
368 require_quiet(peerRef, errOut);
369
370 if(useCompact == kCFBooleanTrue)
371 coder->sessRef = SecOTRSessionCreateFromIDAndFlags(allocator, myRef, peerRef, kSecOTRUseAppleCustomMessageFormat);
372
373 else
374 coder->sessRef = SecOTRSessionCreateFromID(allocator, myRef, peerRef);
375
376 require(coder->sessRef, errOut);
377
378 coder->waitingForDataPacket = false;
379 coder->pendingResponse = NULL;
380
381 CFReleaseNull(publicKey);
382 CFReleaseNull(privateKey);
383 CFReleaseNull(myRef);
384 CFReleaseNull(peerRef);
385 } else {
386 secnotice("coder", "NULL Coder requested, no transport security");
387 }
388
389 coder->hashOfLastReceived = CFDataCreateMutableWithScratch(kCFAllocatorDefault, lastReceived_di()->output_size);
390 coder->lastReceivedWasOld = false;
391
392 SOSCoderStart(coder, NULL);
393
394 return coder;
395
396 errOut:
397 secerror("Coder create failed: %@\n", localError ? localError : (CFTypeRef)CFSTR("No local error in SOSCoderCreate"));
398 secerror("Coder create failed: %@\n", error ? *error : (CFTypeRef)CFSTR("WTF NULL?"));
399 CFReleaseNull(myRef);
400 CFReleaseNull(peerRef);
401 CFReleaseNull(publicKey);
402 CFReleaseNull(privateKey);
403
404 CFReleaseNull(coder);
405 return NULL;
406 }
407
408 static void SOSCoderDestroy(CFTypeRef cf)
409 {
410 SOSCoderRef coder = (SOSCoderRef) cf;
411 if (coder) {
412 CFReleaseNull(coder->sessRef);
413 CFReleaseNull(coder->pendingResponse);
414 CFReleaseNull(coder->hashOfLastReceived);
415 }
416 }
417
418 void SOSCoderReset(SOSCoderRef coder)
419 {
420 SecOTRSessionReset(coder->sessRef);
421 coder->waitingForDataPacket = false;
422 CFReleaseNull(coder->pendingResponse);
423
424 coder->lastReceivedWasOld = false;
425 CFReleaseNull(coder->hashOfLastReceived);
426 coder->hashOfLastReceived = CFDataCreateMutableWithScratch(kCFAllocatorDefault, lastReceived_di()->output_size);
427 }
428
429 bool SOSCoderIsFor(SOSCoderRef coder, SOSPeerInfoRef peerInfo, SOSFullPeerInfoRef myPeerInfo) {
430 SecKeyRef theirPublicKey = NULL;
431 SecKeyRef myPublicKey = NULL;
432 bool isForThisPair = false;
433 CFErrorRef localError = NULL;
434
435 myPublicKey = SOSPeerInfoCopyPubKey(SOSFullPeerInfoGetPeerInfo(myPeerInfo), &localError);
436 require(myPublicKey, errOut);
437
438 theirPublicKey = SOSPeerInfoCopyPubKey(peerInfo, &localError);
439 require(theirPublicKey, errOut);
440
441 isForThisPair = SecOTRSIsForKeys(coder->sessRef, myPublicKey, theirPublicKey);
442
443 errOut:
444 if (localError) {
445 secerror("SOSCoderIsFor failed: %@\n", localError ? localError : (CFTypeRef)CFSTR("No local error in SOSCoderCreate"));
446 }
447
448 CFReleaseNull(myPublicKey);
449 CFReleaseNull(theirPublicKey);
450 CFReleaseNull(localError);
451 return isForThisPair;
452 }
453
454 CFDataRef SOSCoderCopyPendingResponse(SOSCoderRef coder)
455 {
456 return coder->pendingResponse ? CFDataCreateCopy(kCFAllocatorDefault, coder->pendingResponse) : NULL;
457 }
458
459 void SOSCoderConsumeResponse(SOSCoderRef coder)
460 {
461 CFReleaseNull(coder->pendingResponse);
462 }
463
464 static bool SOSOTRSAppendStartPacket(SecOTRSessionRef session, CFMutableDataRef appendPacket, CFErrorRef *error) {
465 OSStatus otrStatus = SecOTRSAppendStartPacket(session, appendPacket);
466 if (otrStatus != errSecSuccess) {
467 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("append start packet returned: %" PRIdOSStatus), otrStatus);
468 }
469 return otrStatus == errSecSuccess;
470 }
471
472 // Start OTR negotiation if we haven't already done so.
473 SOSCoderStatus
474 SOSCoderStart(SOSCoderRef coder, CFErrorRef *error) {
475 CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
476 CFStringRef beginState = NULL;
477 SOSCoderStatus result = kSOSCoderFailure;
478 CFMutableDataRef startPacket = NULL;
479
480 require_action_quiet(coder->sessRef, coderFailure, CFStringAppend(action, CFSTR("*** no otr session ***")));
481 beginState = CFCopyDescription(coder->sessRef);
482 require_action_quiet(!coder->waitingForDataPacket, negotiatingOut, CFStringAppend(action, CFSTR("waiting for peer to send first data packet")));
483 require_action_quiet(!SecOTRSGetIsReadyForMessages(coder->sessRef), coderFailure, CFStringAppend(action, CFSTR("otr session ready"));
484 result = kSOSCoderDataReturned);
485 require_action_quiet(SecOTRSGetIsIdle(coder->sessRef), negotiatingOut, CFStringAppend(action, CFSTR("otr negotiating already")));
486 require_action_quiet(startPacket = CFDataCreateMutable(kCFAllocatorDefault, 0), coderFailure, SOSCreateError(kSOSErrorAllocationFailure, CFSTR("alloc failed"), NULL, error));
487 require_quiet(SOSOTRSAppendStartPacket(coder->sessRef, startPacket, error), coderFailure);
488 CFRetainAssign(coder->pendingResponse, startPacket);
489
490 negotiatingOut:
491 result = kSOSCoderNegotiating;
492 coderFailure:
493 // Uber state log
494 if (result == kSOSCoderFailure && error && *error)
495 CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
496 secinfo("coder", "%@ %s %@ %@ returned %s", beginState,
497 SecOTRPacketTypeString(startPacket), action, coder->sessRef, SOSCoderString(result));
498 CFReleaseNull(startPacket);
499 CFReleaseSafe(beginState);
500 CFRelease(action);
501
502 return result;
503
504 }
505
506 SOSCoderStatus
507 SOSCoderResendDH(SOSCoderRef coder, CFErrorRef *error) {
508 if(coder->sessRef == NULL) return kSOSCoderDataReturned;
509 CFMutableDataRef startPacket = CFDataCreateMutable(kCFAllocatorDefault, 0);
510 SOSCoderStatus result = kSOSCoderFailure;
511 require_noerr_quiet(SecOTRSAppendRestartPacket(coder->sessRef, startPacket), exit);
512 secnotice("coder", "Resending OTR Start %@", startPacket);
513 CFRetainAssign(coder->pendingResponse, startPacket);
514 result = kSOSCoderNegotiating;
515 exit:
516 CFReleaseNull(startPacket);
517 return result;
518 }
519
520
521 static SOSCoderStatus nullCoder(CFDataRef from, CFMutableDataRef *to) {
522 *to = CFDataCreateMutableCopy(NULL, CFDataGetLength(from), from);
523 return kSOSCoderDataReturned;
524 }
525
526 SOSCoderStatus SOSCoderUnwrap(SOSCoderRef coder, CFDataRef codedMessage, CFMutableDataRef *message,
527 CFStringRef clientId, CFErrorRef *error) {
528 if(codedMessage == NULL) return kSOSCoderDataReturned;
529 if(coder->sessRef == NULL) return nullCoder(codedMessage, message);
530 CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
531 /* This should be the "normal" case. We just use OTR to unwrap the received message. */
532 SOSCoderStatus result = kSOSCoderFailure;
533
534 CFStringRef beginState = CFCopyDescription(coder->sessRef);
535 enum SecOTRSMessageKind kind = SecOTRSGetMessageKind(coder->sessRef, codedMessage);
536
537
538 switch (kind) {
539 case kOTRNegotiationPacket: {
540 /* If we're in here we haven't completed negotiating a session. Use SecOTRSProcessPacket() to go through
541 the negotiation steps and immediately send a reply back if necessary using the sendBlock. This
542 assumes the sendBlock is still available.
543 */
544 CFMutableDataRef response = CFDataCreateMutable(kCFAllocatorDefault, 0);
545 OSStatus ppstatus = errSecSuccess;
546 if (response) {
547 switch (ppstatus = SecOTRSProcessPacket(coder->sessRef, codedMessage, response)) {
548 case errSecSuccess:
549 if (CFDataGetLength(response) > 1) {
550 CFStringAppendFormat(action, NULL, CFSTR("Sending OTR Response %s"), SecOTRPacketTypeString(response));
551 CFRetainAssign(coder->pendingResponse, response);
552 result = kSOSCoderNegotiating;
553 if (SecOTRSGetIsReadyForMessages(coder->sessRef)) {
554 CFStringAppend(action, CFSTR(" begin waiting for data packet"));
555 coder->waitingForDataPacket = true;
556 }
557 } else if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) {
558 CFStringAppend(action, CFSTR("stuck?"));
559 result = kSOSCoderNegotiating;
560 } else {
561 CFStringAppend(action, CFSTR("completed negotiation"));
562 result = kSOSCoderNegotiationCompleted;
563 coder->waitingForDataPacket = false;
564 }
565 break;
566 case errSecDecode:
567 CFStringAppend(action, CFSTR("resending dh"));
568 result = SOSCoderResendDH(coder, error);
569 SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.restartotrnegotiation"), 1);
570 break;
571 default:
572 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot negotiate session (%ld)"), clientId, (long)ppstatus);
573 result = kSOSCoderFailure;
574 break;
575 };
576 } else {
577 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ Cannot allocate CFData"), clientId);
578 result = kSOSCoderFailure;
579 }
580
581 CFReleaseNull(response);
582
583 break;
584 }
585
586 case kOTRDataPacket:
587 {
588 CFDataRef previousMessageHash = coder->hashOfLastReceived;
589 coder->hashOfLastReceived = CFDataCreateWithHash(kCFAllocatorDefault, lastReceived_di(), CFDataGetBytePtr(codedMessage), CFDataGetLength(codedMessage));
590 bool lastWasOld = coder->lastReceivedWasOld;
591 coder->lastReceivedWasOld = false;
592
593 if(!SecOTRSGetIsReadyForMessages(coder->sessRef)) {
594 CFStringAppend(action, CFSTR("not ready for data; resending DH packet"));
595 SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.restartotrnegotiation"), 1);
596 result = SOSCoderResendDH(coder, error);
597 } else {
598 if (coder->waitingForDataPacket) {
599 CFStringAppend(action, CFSTR("got data packet we were waiting for "));
600 coder->waitingForDataPacket = false;
601 }
602 CFMutableDataRef exposed = CFDataCreateMutable(0, 0);
603 OSStatus otrResult = SecOTRSVerifyAndExposeMessage(coder->sessRef, codedMessage, exposed);
604 CFStringAppend(action, CFSTR("verify and expose message"));
605 switch(otrResult) {
606 case errSecSuccess:
607 CFStringAppend(action, CFSTR("decoded OTR protected packet"));
608 CFTransferRetained(*message, exposed);
609 result = kSOSCoderDataReturned;
610 break;
611 case errSecOTRTooOld:
612 if (CFEqualSafe(previousMessageHash, coder->hashOfLastReceived)) {
613 CFStringAppend(action, CFSTR(" repeated"));
614 result = kSOSCoderStaleEvent;
615 } else {
616 coder->lastReceivedWasOld = true;
617 if (lastWasOld) {
618 CFStringAppend(action, CFSTR(" too old, repeated renegotiating"));
619 // Fail so we will renegotiate
620 result = kSOSCoderFailure;
621 } else {
622 CFStringAppend(action, CFSTR(" too old, forcing message"));
623 // Force message send.
624 result = kSOSCoderForceMessage;
625 }
626 }
627 break;
628 case errSecOTRIDTooNew:
629 CFStringAppend(action, CFSTR(" too new"));
630 result = kSOSCoderTooNew;
631 break;
632 default:
633 SecError(otrResult, error, CFSTR("%@ Cannot expose message: %" PRIdOSStatus), clientId, otrResult);
634 secerror("%@ Decode OTR Protected Packet: %@", clientId, error ? *error : NULL);
635 result = kSOSCoderFailure;
636 break;
637 }
638
639 CFReleaseNull(exposed);
640 }
641 CFReleaseNull(previousMessageHash);
642 }
643 break;
644
645 default:
646 secerror("%@ Unknown packet type: %@", clientId, codedMessage);
647 SOSCreateError(kSOSErrorDecodeFailure, CFSTR("Unknown packet type"), (error != NULL) ? *error : NULL, error);
648 result = kSOSCoderFailure;
649 break;
650 };
651
652 // Uber state log
653 if (result == kSOSCoderFailure && error && *error)
654 CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
655 secnotice("coder", "%@ %@ %s %@ %@ returned %s", clientId, beginState,
656 SecOTRPacketTypeString(codedMessage), action, coder->sessRef, SOSCoderString(result));
657 CFReleaseSafe(beginState);
658 CFRelease(action);
659
660 return result;
661 }
662
663
664 SOSCoderStatus SOSCoderWrap(SOSCoderRef coder, CFDataRef message, CFMutableDataRef *codedMessage, CFStringRef clientId, CFErrorRef *error) {
665 CFMutableStringRef action = CFStringCreateMutable(kCFAllocatorDefault, 0);
666 SOSCoderStatus result = kSOSCoderDataReturned;
667 CFStringRef beginState = NULL;
668 CFMutableDataRef encoded = NULL;
669 OSStatus otrStatus = 0;
670
671 require_action_quiet(coder->sessRef, errOut,
672 CFStringAppend(action, CFSTR("*** using null coder ***"));
673 result = nullCoder(message, codedMessage));
674 beginState = CFCopyDescription(coder->sessRef);
675 require_action_quiet(SecOTRSGetIsReadyForMessages(coder->sessRef), errOut,
676 CFStringAppend(action, CFSTR("not ready"));
677 result = kSOSCoderNegotiating);
678 require_action_quiet(!coder->waitingForDataPacket, errOut,
679 CFStringAppend(action, CFSTR("waiting for peer to send data packet first"));
680 result = kSOSCoderNegotiating);
681 require_action_quiet(encoded = CFDataCreateMutable(kCFAllocatorDefault, 0), errOut,
682 SOSCreateErrorWithFormat(kSOSErrorAllocationFailure, NULL, error, NULL, CFSTR("%@ alloc failed"), clientId);
683 result = kSOSCoderFailure);
684 require_noerr_action_quiet(otrStatus = SecOTRSSignAndProtectMessage(coder->sessRef, message, encoded), errOut,
685 SOSCreateErrorWithFormat(kSOSErrorEncodeFailure, (error != NULL) ? *error : NULL, error, NULL, CFSTR("%@ cannot protect message: %" PRIdOSStatus), clientId, otrStatus);
686 CFReleaseNull(encoded);
687 result = kSOSCoderFailure);
688 *codedMessage = encoded;
689
690 errOut:
691 // Uber state log
692 if (result == kSOSCoderFailure && error && *error)
693 CFStringAppendFormat(action, NULL, CFSTR(" %@"), *error);
694 secinfo("coder", "%@ %@ %s %@ %@ returned %s", clientId, beginState,
695 SecOTRPacketTypeString(encoded), action, coder->sessRef, SOSCoderString(result));
696 CFReleaseSafe(beginState);
697 CFRelease(action);
698
699 return result;
700 }
701
702 bool SOSCoderCanWrap(SOSCoderRef coder) {
703 return coder->sessRef && SecOTRSGetIsReadyForMessages(coder->sessRef) && !coder->waitingForDataPacket;
704 }