1 /*
2 * Copyright (c) 2015 rcombs
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with FFmpeg; if not, write to the Free Software * Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <errno.h>
22
23
36
37 #include <Security/Security.h>
38 #include <Security/SecureTransport.h>
39 #include <CoreFoundation/CoreFoundation.h>
40
41 // We use a private API call here; it's good enough for WebKit.
42 SecIdentityRef
SecIdentityCreate(CFAllocatorRef allocator, SecCertificateRef certificate, SecKeyRef privateKey);
44
52
54 {
57 case errSSLWouldBlock:
59 case errSSLXCertChainInvalid:
64 default:
67 }
69 }
70
72 {
73 #if !HAVE_SECITEMIMPORT
75 #else
80 SecExternalFormat
format = kSecFormatPEMSequence;
81 SecExternalFormat
type = kSecItemTypeAggregate;
82 CFStringRef pathStr = CFStringCreateWithCString(
NULL, path, 0x08000100);
83 if (!pathStr) {
85 goto end;
86 }
87
89 &
h->interrupt_callback,
NULL,
90 h->protocol_whitelist,
h->protocol_blacklist)) < 0)
91 goto end;
92
94 goto end;
95
98 goto end;
99 }
100
103 goto end;
104 }
105
107 goto end;
108
109 data = CFDataCreate(kCFAllocatorDefault, buf,
ret);
110
114 goto end;
115 }
116
117 if (CFArrayGetCount(*
array) == 0) {
119 goto end;
120 }
121
122 end:
124 if (pathStr)
125 CFRelease(pathStr);
131 #endif
132 }
133
135 {
139
141 goto end;
142
143 if (!(
c->ca_array = CFRetain(
array))) {
145 goto end;
146 }
147
148 end:
152 }
153
155 {
158 CFArrayRef certArray =
NULL;
159 CFArrayRef keyArray =
NULL;
160 SecIdentityRef
id =
NULL;
161 CFMutableArrayRef outArray =
NULL;
162
164 goto end;
165
167 goto end;
168
170 (SecCertificateRef)CFArrayGetValueAtIndex(certArray, 0),
171 (SecKeyRef)CFArrayGetValueAtIndex(keyArray, 0)))) {
173 goto end;
174 }
175
176 if (!(outArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, certArray))) {
178 goto end;
179 }
180
181 CFArraySetValueAtIndex(outArray, 0, id);
182
183 SSLSetCertificate(
c->ssl_context, outArray);
184
185 end:
186 if (certArray)
187 CFRelease(certArray);
188 if (keyArray)
189 CFRelease(keyArray);
190 if (outArray)
191 CFRelease(outArray);
192 if (id)
193 CFRelease(id);
195 }
196
197 static OSStatus
tls_read_cb(SSLConnectionRef connection,
void *
data,
size_t *dataLength)
198 {
201 size_t requested = *dataLength;
204 *dataLength = 0;
206 case ENOENT:
207 case 0:
208 return errSSLClosedGraceful;
209 case ECONNRESET:
210 return errSSLClosedAbort;
211 case EAGAIN:
212 return errSSLWouldBlock;
213 default:
216 }
217 } else {
219 if (
read < requested)
220 return errSSLWouldBlock;
221 else
222 return noErr;
223 }
224 }
225
226 static OSStatus
tls_write_cb(SSLConnectionRef connection,
const void *
data,
size_t *dataLength)
227 {
231 if (written <= 0) {
232 *dataLength = 0;
234 case EAGAIN:
235 return errSSLWouldBlock;
236 default:
237 c->lastErr = written;
239 }
240 } else {
241 *dataLength = written;
242 return noErr;
243 }
244 }
245
247 {
249 if (
c->ssl_context) {
250 SSLClose(
c->ssl_context);
251 CFRelease(
c->ssl_context);
252 }
254 CFRelease(
c->ca_array);
256 return 0;
257 }
258
259 #define CHECK_ERROR(func, ...) do { \
260 OSStatus status = func(__VA_ARGS__); \
261 if (status != noErr) { \
262 ret = AVERROR_UNKNOWN; \
263 av_log(h, AV_LOG_ERROR, #func ": Error %i\n", (int)status); \
264 goto fail; \
265 } \
266 } while (0)
267
269 {
273
276
277 c->ssl_context = SSLCreateContext(
NULL,
s->listen ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
278 if (!
c->ssl_context) {
282 }
286 }
287 if (
s->ca_file || !
s->verify)
288 CHECK_ERROR(SSLSetSessionOption,
c->ssl_context, kSSLSessionOptionBreakOnServerAuth,
true);
292 CHECK_ERROR(SSLSetPeerDomainName,
c->ssl_context,
s->host, strlen(
s->host));
295 while (1) {
296 OSStatus
status = SSLHandshake(
c->ssl_context);
297 if (
status == errSSLServerAuthCompleted) {
298 SecTrustRef peerTrust;
299 SecTrustResultType trustResult;
301 continue;
302
303 if (SSLCopyPeerTrust(
c->ssl_context, &peerTrust) != noErr) {
306 }
307
308 if (SecTrustSetAnchorCertificates(peerTrust,
c->ca_array) != noErr) {
311 }
312
313 if (SecTrustEvaluate(peerTrust, &trustResult) != noErr) {
316 }
317
318 if (trustResult == kSecTrustResultProceed ||
319 trustResult == kSecTrustResultUnspecified) {
320 // certificate is trusted
321 status = errSSLWouldBlock;
// so we call SSLHandshake again
322 } else if (trustResult == kSecTrustResultRecoverableTrustFailure) {
323 // not trusted, for some reason other than being expired
324 status = errSSLXCertChainInvalid;
325 } else {
326 // cannot use this certificate (fatal)
328 }
329
330 if (peerTrust)
331 CFRelease(peerTrust);
332 }
334 break;
335 }
else if (
status != errSSLWouldBlock) {
339 }
340 }
341
342 return 0;
346 }
347
349 {
351 case noErr:
353 case errSSLClosedGraceful:
354 case errSSLClosedNoNotify:
355 return 0;
356 case errSSLWouldBlock:
359 default:
361 }
362 }
363
365 {
369 SSLGetBufferedReadSize(
c->ssl_context, &
available);
379 }
380
382 {
392 }
393
395 {
398 }
399
401 {
404 }
405
409 };
410
416 };
417
429 };