Update translations from 2.2.x branch
[vlc.git] / modules / misc / securetransport.c
blob74d4b9cdc70c076d4033a45435f16383dae89272
1 /*****************************************************************************
2 * securetransport.c
3 *****************************************************************************
4 * Copyright (C) 2013 David Fuhrmann
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program 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.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 /*****************************************************************************
22 * Preamble
23 *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_tls.h>
32 #include <vlc_dialog.h>
34 #include <Security/Security.h>
35 #include <Security/SecureTransport.h>
36 #include <TargetConditionals.h>
38 /* From MacErrors.h (cannot be included because it isn't present in iOS: */
39 #ifndef ioErr
40 # define ioErr -36
41 #endif
43 /*****************************************************************************
44 * Module descriptor
45 *****************************************************************************/
46 static int OpenClient (vlc_tls_creds_t *);
47 static void CloseClient (vlc_tls_creds_t *);
49 #if !TARGET_OS_IPHONE
50 static int OpenServer (vlc_tls_creds_t *crd, const char *cert, const char *key);
51 static void CloseServer (vlc_tls_creds_t *);
52 #endif
54 vlc_module_begin ()
55 set_description(N_("TLS support for OS X and iOS"))
56 set_capability("tls client", 2)
57 set_callbacks(OpenClient, CloseClient)
58 set_category(CAT_ADVANCED)
59 set_subcategory(SUBCAT_ADVANCED_NETWORK)
62 * The server module currently uses an OSX only API, to be compatible with 10.6.
63 If the module is needed on iOS, then the "modern" keychain lookup API need to be
64 * implemented.
66 #if !TARGET_OS_IPHONE
67 add_submodule()
68 set_description(N_("TLS server support for OS X"))
69 set_capability("tls server", 2)
70 set_callbacks(OpenServer, CloseServer)
71 set_category(CAT_ADVANCED)
72 set_subcategory(SUBCAT_ADVANCED_NETWORK)
73 #endif /* !TARGET_OS_IPHONE */
75 vlc_module_end ()
78 #define cfKeyHost CFSTR("host")
79 #define cfKeyCertificate CFSTR("certificate")
81 typedef struct {
82 CFMutableArrayRef whitelist;
84 /* valid in server mode */
85 CFArrayRef server_cert_chain;
86 } vlc_tls_creds_sys_t;
88 typedef struct {
89 vlc_tls_t tls;
90 SSLContextRef p_context;
91 vlc_tls_creds_sys_t *p_cred;
92 size_t i_send_buffered_bytes;
93 vlc_tls_t *sock;
94 vlc_object_t *obj;
96 bool b_blocking_send;
97 bool b_handshaked;
98 bool b_server_mode;
99 } vlc_tls_st_t;
101 static int st_Error (vlc_tls_t *obj, int val)
103 vlc_tls_st_t *sys = (vlc_tls_st_t *)obj;
105 switch (val)
107 case errSSLWouldBlock:
108 errno = EAGAIN;
109 break;
111 case errSSLClosedGraceful:
112 case errSSLClosedAbort:
113 msg_Dbg(sys->obj, "Connection closed with code %d", val);
114 errno = ECONNRESET;
115 break;
116 default:
117 msg_Err(sys->obj, "Found error %d", val);
118 errno = ECONNRESET;
120 return -1;
124 * Read function called by secure transport for socket read.
126 * Function is based on Apples SSLSample sample code.
128 static OSStatus st_SocketReadFunc (SSLConnectionRef connection,
129 void *data,
130 size_t *dataLength) {
132 vlc_tls_t *session = (vlc_tls_t *)connection;
133 vlc_tls_st_t *sys = (vlc_tls_st_t *)session;
134 struct iovec iov = {
135 .iov_base = data,
136 .iov_len = *dataLength,
138 OSStatus retValue = noErr;
140 while (iov.iov_len > 0) {
141 ssize_t val = sys->sock->readv(sys->sock, &iov, 1);
142 if (val <= 0) {
143 if (val == 0) {
144 msg_Dbg(sys->obj, "found eof");
145 retValue = errSSLClosedGraceful;
146 } else { /* do the switch */
147 switch (errno) {
148 case ENOENT:
149 /* connection closed */
150 retValue = errSSLClosedGraceful;
151 break;
152 case ECONNRESET:
153 retValue = errSSLClosedAbort;
154 break;
155 case EAGAIN:
156 retValue = errSSLWouldBlock;
157 sys->b_blocking_send = false;
158 break;
159 default:
160 msg_Err(sys->obj, "try to read %zu bytes, "
161 "got error %d", iov.iov_len, errno);
162 retValue = ioErr;
163 break;
166 break;
169 iov.iov_base = (char *)iov.iov_base + val;
170 iov.iov_len -= val;
173 *dataLength -= iov.iov_len;
174 return retValue;
178 * Write function called by secure transport for socket read.
180 * Function is based on Apples SSLSample sample code.
182 static OSStatus st_SocketWriteFunc (SSLConnectionRef connection,
183 const void *data,
184 size_t *dataLength) {
186 vlc_tls_t *session = (vlc_tls_t *)connection;
187 vlc_tls_st_t *sys = (vlc_tls_st_t *)session;
188 struct iovec iov = {
189 .iov_base = (void *)data,
190 .iov_len = *dataLength,
192 OSStatus retValue = noErr;
194 while (iov.iov_len > 0) {
195 ssize_t val = sys->sock->writev(sys->sock, &iov, 1);
196 if (val < 0) {
197 switch (errno) {
198 case EAGAIN:
199 retValue = errSSLWouldBlock;
200 sys->b_blocking_send = true;
201 break;
203 case EPIPE:
204 case ECONNRESET:
205 retValue = errSSLClosedAbort;
206 break;
208 default:
209 msg_Err(sys->obj, "error while writing: %d", errno);
210 retValue = ioErr;
211 break;
213 break;
216 iov.iov_base = (char *)iov.iov_base + val;
217 iov.iov_len -= val;
220 *dataLength -= iov.iov_len;
221 return retValue;
224 static int st_validateServerCertificate (vlc_tls_t *session, const char *hostname) {
226 vlc_tls_st_t *sys = (vlc_tls_st_t *)session;
227 int result = -1;
228 SecCertificateRef leaf_cert = NULL;
230 SecTrustRef trust = NULL;
231 OSStatus ret = SSLCopyPeerTrust(sys->p_context, &trust);
232 if (ret != noErr || trust == NULL) {
233 msg_Err(sys->obj, "error getting certifictate chain");
234 return -1;
237 CFStringRef cfHostname = CFStringCreateWithCString(kCFAllocatorDefault,
238 hostname,
239 kCFStringEncodingUTF8);
242 /* enable default root / anchor certificates */
243 ret = SecTrustSetAnchorCertificates(trust, NULL);
244 if (ret != noErr) {
245 msg_Err(sys->obj, "error setting anchor certificates");
246 result = -1;
247 goto out;
250 SecTrustResultType trust_eval_result = 0;
252 ret = SecTrustEvaluate(trust, &trust_eval_result);
253 if (ret != noErr) {
254 msg_Err(sys->obj, "error calling SecTrustEvaluate");
255 result = -1;
256 goto out;
259 switch (trust_eval_result) {
260 case kSecTrustResultUnspecified:
261 case kSecTrustResultProceed:
262 msg_Dbg(sys->obj, "cerfificate verification successful, result is %d", trust_eval_result);
263 result = 0;
264 goto out;
266 case kSecTrustResultRecoverableTrustFailure:
267 case kSecTrustResultDeny:
268 default:
269 msg_Warn(sys->obj, "cerfificate verification failed, result is %d", trust_eval_result);
272 /* get leaf certificate */
273 /* SSLCopyPeerCertificates is only available on OSX 10.5 or later */
274 #if !TARGET_OS_IPHONE
275 CFArrayRef cert_chain = NULL;
276 ret = SSLCopyPeerCertificates(sys->p_context, &cert_chain);
277 if (ret != noErr || !cert_chain) {
278 result = -1;
279 goto out;
282 if (CFArrayGetCount(cert_chain) == 0) {
283 CFRelease(cert_chain);
284 result = -1;
285 goto out;
288 leaf_cert = (SecCertificateRef)CFArrayGetValueAtIndex(cert_chain, 0);
289 CFRetain(leaf_cert);
290 CFRelease(cert_chain);
291 #else
292 /* SecTrustGetCertificateAtIndex is only available on 10.7 or iOS */
293 if (SecTrustGetCertificateCount(trust) == 0) {
294 result = -1;
295 goto out;
298 leaf_cert = SecTrustGetCertificateAtIndex(trust, 0);
299 CFRetain(leaf_cert);
300 #endif
303 /* check if leaf already accepted */
304 CFIndex max = CFArrayGetCount(sys->p_cred->whitelist);
305 for (CFIndex i = 0; i < max; ++i) {
306 CFDictionaryRef dict = CFArrayGetValueAtIndex(sys->p_cred->whitelist, i);
307 CFStringRef knownHost = (CFStringRef)CFDictionaryGetValue(dict, cfKeyHost);
308 SecCertificateRef knownCert = (SecCertificateRef)CFDictionaryGetValue(dict, cfKeyCertificate);
310 if (!knownHost || !knownCert)
311 continue;
313 if (CFEqual(knownHost, cfHostname) && CFEqual(knownCert, leaf_cert)) {
314 msg_Warn(sys->obj, "certificate already accepted, continuing");
315 result = 0;
316 goto out;
320 /* We do not show more certificate details yet because there is no proper API to get
321 a summary of the certificate. SecCertificateCopySubjectSummary is the only method
322 available on iOS and 10.6. More promising API functions such as
323 SecCertificateCopyLongDescription also print out the subject only, more or less.
324 But only showing the certificate subject is of no real help for the user.
325 We could use SecCertificateCopyValues, but then we need to parse all OID values for
326 ourself. This is too mad for just printing information the user will never check
327 anyway.
330 const char *msg = N_("You attempted to reach %s. "
331 "However the security certificate presented by the server "
332 "is unknown and could not be authenticated by any trusted "
333 "Certification Authority. "
334 "This problem may be caused by a configuration error "
335 "or an attempt to breach your security or your privacy.\n\n"
336 "If in doubt, abort now.\n");
337 int answer = vlc_dialog_wait_question(sys->obj,
338 VLC_DIALOG_QUESTION_WARNING, _("Abort"),
339 _("Accept certificate temporarily"),
340 NULL, _("Insecure site"),
341 vlc_gettext (msg), hostname);
342 if (answer == 1) {
343 msg_Warn(sys->obj, "Proceeding despite of failed certificate validation");
345 /* save leaf certificate in whitelist */
346 const void *keys[] = {cfKeyHost, cfKeyCertificate};
347 const void *values[] = {cfHostname, leaf_cert};
348 CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault,
349 keys, values, 2,
350 &kCFTypeDictionaryKeyCallBacks,
351 &kCFTypeDictionaryValueCallBacks);
352 if (!dict) {
353 msg_Err(sys->obj, "error creating dict");
354 result = -1;
355 goto out;
358 CFArrayAppendValue(sys->p_cred->whitelist, dict);
359 CFRelease(dict);
361 result = 0;
362 goto out;
364 } else {
365 result = -1;
366 goto out;
369 out:
370 CFRelease(trust);
372 if (cfHostname)
373 CFRelease(cfHostname);
374 if (leaf_cert)
375 CFRelease(leaf_cert);
377 return result;
381 * @return -1 on fatal error, 0 on successful handshake completion,
382 * 1 if more would-be blocking recv is needed,
383 * 2 if more would-be blocking send is required.
385 static int st_Handshake (vlc_tls_creds_t *crd, vlc_tls_t *session,
386 const char *host, const char *service,
387 char **restrict alp) {
389 vlc_tls_st_t *sys = (vlc_tls_st_t *)session;
391 VLC_UNUSED(service);
393 OSStatus retValue = SSLHandshake(sys->p_context);
394 if (alp != NULL) {
395 *alp = NULL;
398 if (retValue == errSSLWouldBlock) {
399 msg_Dbg(crd, "handshake is blocked, try again later");
400 return 1 + (sys->b_blocking_send ? 1 : 0);
403 switch (retValue) {
404 case noErr:
405 if (sys->b_server_mode == false && st_validateServerCertificate(session, host) != 0) {
406 return -1;
408 msg_Dbg(crd, "handshake completed successfully");
409 sys->b_handshaked = true;
410 return 0;
412 case errSSLServerAuthCompleted:
413 msg_Dbg(crd, "SSLHandshake returned errSSLServerAuthCompleted, continuing handshake");
414 return st_Handshake(crd, session, host, service, alp);
416 case errSSLConnectionRefused:
417 msg_Err(crd, "connection was refused");
418 return -1;
419 case errSSLNegotiation:
420 msg_Err(crd, "cipher suite negotiation failed");
421 return -1;
422 case errSSLFatalAlert:
423 msg_Err(crd, "fatal error occurred during handshake");
424 return -1;
426 default:
427 msg_Err(crd, "handshake returned error %d", (int)retValue);
428 return -1;
432 static int st_GetFD (vlc_tls_t *session)
434 vlc_tls_st_t *sys = (vlc_tls_st_t *)session;
435 vlc_tls_t *sock = sys->sock;
437 return vlc_tls_GetFD(sock);
441 * Sends data through a TLS session.
443 static ssize_t st_Send (vlc_tls_t *session, const struct iovec *iov,
444 unsigned count)
446 vlc_tls_st_t *sys = (vlc_tls_st_t *)session;
447 OSStatus ret = noErr;
449 if (unlikely(count == 0))
450 return 0;
453 * SSLWrite does not return the number of bytes actually written to
454 * the socket, but the number of bytes written to the internal cache.
456 * If return value is errSSLWouldBlock, the underlying socket cannot
457 * send all data, but the data is already cached. In this situation,
458 * we need to call SSLWrite again. To ensure this call even for the
459 * last bytes, we return EAGAIN. On the next call, we give no new data
460 * to SSLWrite until the error is not errSSLWouldBlock anymore.
462 * This code is adapted the same way as done in curl.
463 * (https://github.com/bagder/curl/blob/master/lib/curl_darwinssl.c#L2067)
466 /* EAGAIN is not expected by net_Write in this situation,
467 so use EINTR here */
468 int againErr = sys->b_server_mode ? EAGAIN : EINTR;
470 size_t actualSize;
471 if (sys->i_send_buffered_bytes > 0) {
472 ret = SSLWrite(sys->p_context, NULL, 0, &actualSize);
474 if (ret == noErr) {
475 /* actualSize remains zero because no new data send */
476 actualSize = sys->i_send_buffered_bytes;
477 sys->i_send_buffered_bytes = 0;
479 } else if (ret == errSSLWouldBlock) {
480 errno = againErr;
481 return -1;
484 } else {
485 ret = SSLWrite(sys->p_context, iov->iov_base, iov->iov_len,
486 &actualSize);
488 if (ret == errSSLWouldBlock) {
489 sys->i_send_buffered_bytes = iov->iov_len;
490 errno = againErr;
491 return -1;
495 return ret != noErr ? st_Error(session, ret) : actualSize;
499 * Receives data through a TLS session.
501 static ssize_t st_Recv (vlc_tls_t *session, struct iovec *iov, unsigned count)
503 vlc_tls_st_t *sys = (vlc_tls_st_t *)session;
505 if (unlikely(count == 0))
506 return 0;
508 size_t actualSize;
509 OSStatus ret = SSLRead(sys->p_context, iov->iov_base, iov->iov_len,
510 &actualSize);
512 if (ret == errSSLWouldBlock && actualSize)
513 return actualSize;
515 /* peer performed shutdown */
516 if (ret == errSSLClosedNoNotify || ret == errSSLClosedGraceful) {
517 msg_Dbg(sys->obj, "Got close notification with code %i", (int)ret);
518 return 0;
521 return ret != noErr ? st_Error(session, ret) : actualSize;
525 * Closes a TLS session.
528 static int st_SessionShutdown (vlc_tls_t *session, bool duplex) {
530 vlc_tls_st_t *sys = (vlc_tls_st_t *)session;
532 msg_Dbg(sys->obj, "shutdown TLS session");
534 OSStatus ret = noErr;
535 VLC_UNUSED(duplex);
537 if (sys->b_handshaked) {
538 ret = SSLClose(sys->p_context);
541 if (ret != noErr) {
542 msg_Warn(sys->obj, "Cannot close ssl context (%i)", (int)ret);
543 return ret;
546 return 0;
549 static void st_SessionClose (vlc_tls_t *session) {
551 vlc_tls_st_t *sys = (vlc_tls_st_t *)session;
552 msg_Dbg(sys->obj, "close TLS session");
554 if (sys->p_context) {
555 #if TARGET_OS_IPHONE
556 CFRelease(sys->p_context);
557 #else
558 if (SSLDisposeContext(sys->p_context) != noErr) {
559 msg_Err(sys->obj, "error deleting context");
561 #endif
563 free(sys);
567 * Initializes a client-side TLS session.
570 static vlc_tls_t *st_SessionOpenCommon(vlc_tls_creds_t *crd, vlc_tls_t *sock,
571 bool b_server)
573 vlc_tls_st_t *sys = malloc(sizeof (*sys));
574 if (unlikely(sys == NULL))
575 return NULL;
577 sys->p_cred = crd->sys;
578 sys->b_handshaked = false;
579 sys->b_blocking_send = false;
580 sys->i_send_buffered_bytes = 0;
581 sys->p_context = NULL;
582 sys->sock = sock;
583 sys->b_server_mode = b_server;
584 sys->obj = VLC_OBJECT(crd);
586 vlc_tls_t *tls = &sys->tls;
588 tls->get_fd = st_GetFD;
589 tls->readv = st_Recv;
590 tls->writev = st_Send;
591 tls->shutdown = st_SessionShutdown;
592 tls->close = st_SessionClose;
593 crd->handshake = st_Handshake;
595 SSLContextRef p_context = NULL;
596 #if TARGET_OS_IPHONE
597 p_context = SSLCreateContext(NULL, b_server ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
598 if (p_context == NULL) {
599 msg_Err(crd, "cannot create ssl context");
600 goto error;
602 #else
603 if (SSLNewContext(b_server, &p_context) != noErr) {
604 msg_Err(crd, "error calling SSLNewContext");
605 goto error;
607 #endif
609 sys->p_context = p_context;
611 OSStatus ret = SSLSetIOFuncs(p_context, st_SocketReadFunc, st_SocketWriteFunc);
612 if (ret != noErr) {
613 msg_Err(crd, "cannot set io functions");
614 goto error;
617 ret = SSLSetConnection(p_context, tls);
618 if (ret != noErr) {
619 msg_Err(crd, "cannot set connection");
620 goto error;
623 return tls;
625 error:
626 st_SessionClose(tls);
627 return NULL;
630 static vlc_tls_t *st_ClientSessionOpen(vlc_tls_creds_t *crd, vlc_tls_t *sock,
631 const char *hostname, const char *const *alpn)
633 if (alpn != NULL) {
634 msg_Warn(crd, "Ignoring ALPN request due to lack of support in the backend. Proxy behavior potentially undefined.");
635 #warning ALPN support missing, proxy behavior potentially undefined (rdar://29127318, #17721)
638 msg_Dbg(crd, "open TLS session for %s", hostname);
640 vlc_tls_t *tls = st_SessionOpenCommon(crd, sock, false);
641 if (tls == NULL)
642 return NULL;
644 vlc_tls_st_t *sys = (vlc_tls_st_t *)tls;
646 OSStatus ret = SSLSetPeerDomainName(sys->p_context, hostname, strlen(hostname));
647 if (ret != noErr) {
648 msg_Err(crd, "cannot set peer domain name");
649 goto error;
652 /* disable automatic validation. We do so manually to also handle invalid
653 certificates */
655 /* this has effect only on iOS 5 and OSX 10.8 or later ... */
656 ret = SSLSetSessionOption(sys->p_context, kSSLSessionOptionBreakOnServerAuth, true);
657 if (ret != noErr) {
658 msg_Err (crd, "cannot set session option");
659 goto error;
661 #if !TARGET_OS_IPHONE
662 /* ... thus calling this for earlier osx versions, which is not available on iOS in turn */
663 ret = SSLSetEnableCertVerify(sys->p_context, false);
664 if (ret != noErr) {
665 msg_Err(crd, "error setting enable cert verify");
666 goto error;
668 #endif
670 return tls;
672 error:
673 st_SessionShutdown(tls, true);
674 st_SessionClose(tls);
675 return NULL;
679 * Initializes a client-side TLS credentials.
681 static int OpenClient (vlc_tls_creds_t *crd) {
683 msg_Dbg(crd, "open st client");
685 vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
686 if (unlikely(sys == NULL))
687 return VLC_ENOMEM;
689 sys->whitelist = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
690 sys->server_cert_chain = NULL;
692 crd->sys = sys;
693 crd->open = st_ClientSessionOpen;
695 return VLC_SUCCESS;
698 static void CloseClient (vlc_tls_creds_t *crd) {
699 msg_Dbg(crd, "close secure transport client");
701 vlc_tls_creds_sys_t *sys = crd->sys;
703 if (sys->whitelist)
704 CFRelease(sys->whitelist);
706 free(sys);
709 /* Begin of server-side methods */
710 #if !TARGET_OS_IPHONE
713 * Initializes a server-side TLS session.
715 static vlc_tls_t *st_ServerSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *sock,
716 const char *hostname, const char *const *alpn) {
718 VLC_UNUSED(hostname);
719 VLC_UNUSED(alpn);
720 msg_Dbg(crd, "open TLS server session");
722 vlc_tls_t *tls = st_SessionOpenCommon(crd, sock, true);
723 if (tls != NULL)
724 return NULL;
726 vlc_tls_st_t *sys = (vlc_tls_st_t *)tls;
727 vlc_tls_creds_sys_t *p_cred_sys = crd->sys;
729 OSStatus ret = SSLSetCertificate(sys->p_context, p_cred_sys->server_cert_chain);
730 if (ret != noErr) {
731 msg_Err(crd, "cannot set server certificate");
732 goto error;
735 return tls;
737 error:
738 st_SessionShutdown(tls, true);
739 st_SessionClose(tls);
740 return NULL;
744 * Initializes server-side TLS credentials.
746 static int OpenServer (vlc_tls_creds_t *crd, const char *cert, const char *key) {
749 * This function expects the label of the certificate in "cert", stored
750 * in the MacOS keychain. The appropriate private key is found automatically.
752 VLC_UNUSED(key);
753 OSStatus ret;
755 msg_Dbg(crd, "open st server");
758 * Get the server certificate.
760 * This API is deprecated, but the replacement SecItemCopyMatching
761 * only works on >= 10.7
763 SecKeychainAttribute attrib = { kSecLabelItemAttr, strlen(cert), (void *)cert };
764 SecKeychainAttributeList attrList = { 1, &attrib };
766 SecKeychainSearchRef searchReference = NULL;
767 ret = SecKeychainSearchCreateFromAttributes(NULL, kSecCertificateItemClass,
768 &attrList, &searchReference);
769 if (ret != noErr || searchReference == NULL) {
770 msg_Err(crd, "Cannot find certificate with alias %s", cert);
771 return VLC_EGENERIC;
774 SecKeychainItemRef itemRef = NULL;
775 ret = SecKeychainSearchCopyNext(searchReference, &itemRef);
776 if (ret != noErr) {
777 msg_Err(crd, "Cannot get certificate with alias %s, error: %d", cert, ret);
778 return VLC_EGENERIC;
780 CFRelease(searchReference);
782 /* cast allowed according to documentation */
783 SecCertificateRef certificate = (SecCertificateRef)itemRef;
785 SecIdentityRef cert_identity = NULL;
786 ret = SecIdentityCreateWithCertificate(NULL, certificate, &cert_identity);
787 if (ret != noErr) {
788 msg_Err(crd, "Cannot get private key for certificate");
789 CFRelease(certificate);
790 return VLC_EGENERIC;
794 * We try to validate the server certificate, but do not care about the result.
795 * The only aim is to get the certificate chain.
797 SecPolicyRef policy = SecPolicyCreateSSL(true, NULL);
798 SecTrustRef trust_ref = NULL;
799 int result = VLC_SUCCESS;
801 /* According to docu its fine to pass just one certificate */
802 ret = SecTrustCreateWithCertificates((CFArrayRef)certificate, policy, &trust_ref);
803 if (ret != noErr) {
804 msg_Err(crd, "Cannot create trust");
805 result = VLC_EGENERIC;
806 goto out;
809 SecTrustResultType status;
810 ret = SecTrustEvaluate(trust_ref, &status);
811 if (ret != noErr) {
812 msg_Err(crd, "Error evaluating trust");
813 result = VLC_EGENERIC;
814 goto out;
817 CFArrayRef cert_chain = NULL;
818 CSSM_TP_APPLE_EVIDENCE_INFO *status_chain;
819 ret = SecTrustGetResult(trust_ref, &status, &cert_chain, &status_chain);
820 if (ret != noErr || !cert_chain) {
821 msg_Err(crd, "error while getting certificate chain");
822 result = VLC_EGENERIC;
823 goto out;
826 CFIndex num_cert_chain = CFArrayGetCount(cert_chain);
828 /* Build up the certificate chain array expected by SSLSetCertificate */
829 CFMutableArrayRef server_cert_chain = CFArrayCreateMutable(kCFAllocatorDefault, num_cert_chain, &kCFTypeArrayCallBacks);
830 CFArrayAppendValue(server_cert_chain, cert_identity);
832 msg_Dbg(crd, "Found certificate chain with %ld entries for server certificate", num_cert_chain);
833 if (num_cert_chain > 1)
834 CFArrayAppendArray(server_cert_chain, cert_chain, CFRangeMake(1, num_cert_chain - 1));
835 CFRelease(cert_chain);
837 vlc_tls_creds_sys_t *sys = malloc(sizeof(*sys));
838 if (unlikely(sys == NULL)) {
839 CFRelease(server_cert_chain);
840 result = VLC_ENOMEM;
841 goto out;
844 sys->server_cert_chain = server_cert_chain;
845 sys->whitelist = NULL;
847 crd->sys = sys;
848 crd->open = st_ServerSessionOpen;
850 out:
851 if (policy)
852 CFRelease(policy);
853 if (trust_ref)
854 CFRelease(trust_ref);
856 if (certificate)
857 CFRelease(certificate);
858 if (cert_identity)
859 CFRelease(cert_identity);
861 return result;
864 static void CloseServer (vlc_tls_creds_t *crd) {
865 msg_Dbg(crd, "close secure transport server");
867 vlc_tls_creds_sys_t *sys = crd->sys;
869 if (sys->server_cert_chain)
870 CFRelease(sys->server_cert_chain);
872 free(sys);
875 #endif /* !TARGET_OS_IPHONE */