]> git.sesse.net Git - vlc/blob - modules/misc/securetransport.c
News: fix typo found by No_se
[vlc] / modules / misc / securetransport.c
1 /*****************************************************************************
2  * securetransport.c
3  *****************************************************************************
4  * Copyright (C) 2013 David Fuhrmann
5  *
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.
10  *
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.
15  *
16  * You should have received a copy of the GNU Ă–esser 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  *****************************************************************************/
20
21 /*****************************************************************************
22  * Preamble
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_tls.h>
32 #include <vlc_dialog.h>
33
34 #include <Security/Security.h>
35 #include <Security/SecureTransport.h>
36 #include <TargetConditionals.h>
37
38 /* From MacErrors.h (cannot be included because it isn't present in iOS: */
39 #ifndef ioErr
40 # define ioErr -36
41 #endif
42
43 /*****************************************************************************
44  * Module descriptor
45  *****************************************************************************/
46 static int  OpenClient  (vlc_tls_creds_t *);
47 static void CloseClient (vlc_tls_creds_t *);
48
49 static int  OpenServer  (vlc_tls_creds_t *crd, const char *cert, const char *key);
50 static void CloseServer (vlc_tls_creds_t *);
51
52 vlc_module_begin ()
53     set_description(N_("TLS support for OS X and iOS"))
54     set_capability("tls client", 2)
55     set_callbacks(OpenClient, CloseClient)
56     set_category(CAT_ADVANCED)
57     set_subcategory(SUBCAT_ADVANCED_NETWORK)
58
59     /*
60      * The server module currently uses an OSX only API, to be compatible with 10.6.
61      * If the module is needed on iOS, then the "modern" keychain lookup API need to be
62      * implemented.
63      */
64 #if !TARGET_OS_IPHONE
65     add_submodule()
66         set_description(N_("TLS server support for OS X"))
67         set_capability("tls server", 2)
68         set_callbacks(OpenServer, CloseServer)
69         set_category(CAT_ADVANCED)
70         set_subcategory(SUBCAT_ADVANCED_NETWORK)
71 #endif /* !TARGET_OS_IPHONE */
72
73 vlc_module_end ()
74
75
76 #define cfKeyHost CFSTR("host")
77 #define cfKeyCertificate CFSTR("certificate")
78
79 struct vlc_tls_creds_sys
80 {
81     CFMutableArrayRef whitelist;
82
83     /* valid in server mode */
84     CFArrayRef server_cert_chain;
85 };
86
87 struct vlc_tls_sys {
88     SSLContextRef p_context;
89     vlc_tls_creds_sys_t *p_cred;
90     size_t i_send_buffered_bytes;
91     int i_fd;
92
93     bool b_blocking_send;
94     bool b_handshaked;
95     bool b_server_mode;
96 };
97
98 static int st_Error (vlc_tls_t *obj, int val)
99 {
100     switch (val)
101     {
102         case errSSLWouldBlock:
103             errno = EAGAIN;
104             break;
105
106         case errSSLClosedGraceful:
107         case errSSLClosedAbort:
108             msg_Dbg(obj, "Connection closed with code %d", val);
109             errno = ECONNRESET;
110             break;
111         default:
112             msg_Err(obj, "Found error %d", val);
113             errno = ECONNRESET;
114     }
115     return -1;
116 }
117
118 /*
119  * Read function called by secure transport for socket read.
120  *
121  * Function is based on Apples SSLSample sample code.
122  */
123 static OSStatus st_SocketReadFunc (SSLConnectionRef connection,
124                                    void *data,
125                                    size_t *dataLength) {
126
127     vlc_tls_t *session = (vlc_tls_t *)connection;
128     vlc_tls_sys_t *sys = session->sys;
129
130     size_t bytesToGo = *dataLength;
131     size_t initLen = bytesToGo;
132     UInt8 *currData = (UInt8 *)data;
133     OSStatus retValue = noErr;
134     ssize_t val;
135
136     for (;;) {
137         val = read(sys->i_fd, currData, bytesToGo);
138         if (val <= 0) {
139             if (val == 0) {
140                 msg_Dbg(session, "found eof");
141                 retValue = errSSLClosedGraceful;
142             } else { /* do the switch */
143                 switch (errno) {
144                     case ENOENT:
145                         /* connection closed */
146                         retValue = errSSLClosedGraceful;
147                         break;
148                     case ECONNRESET:
149                         retValue = errSSLClosedAbort;
150                         break;
151                     case EAGAIN:
152                         retValue = errSSLWouldBlock;
153                         sys->b_blocking_send = false;
154                         break;
155                     default:
156                         msg_Err(session, "try to read %d bytes, got error %d",
157                                 (int)bytesToGo, errno);
158                         retValue = ioErr;
159                         break;
160                 }
161             }
162             break;
163         } else {
164             bytesToGo -= val;
165             currData += val;
166         }
167
168         if (bytesToGo == 0) {
169             /* filled buffer with incoming data, done */
170             break;
171         }
172     }
173     *dataLength = initLen - bytesToGo;
174
175     return retValue;
176 }
177
178 /*
179  * Write function called by secure transport for socket read.
180  *
181  * Function is based on Apples SSLSample sample code.
182  */
183 static OSStatus st_SocketWriteFunc (SSLConnectionRef connection,
184                                     const void *data,
185                                     size_t *dataLength) {
186
187     vlc_tls_t *session = (vlc_tls_t *)connection;
188     vlc_tls_sys_t *sys = session->sys;
189
190     size_t bytesSent = 0;
191     size_t dataLen = *dataLength;
192     OSStatus retValue = noErr;
193     ssize_t val;
194
195     do {
196         val = write(sys->i_fd, (char *)data + bytesSent, dataLen - bytesSent);
197     } while (val >= 0 && (bytesSent += val) < dataLen);
198
199     if (val < 0) {
200         switch(errno) {
201             case EAGAIN:
202                 retValue = errSSLWouldBlock;
203                 sys->b_blocking_send = true;
204                 break;
205
206             case EPIPE:
207             case ECONNRESET:
208                 retValue = errSSLClosedAbort;
209                 break;
210
211             default:
212                 msg_Err(session, "error while writing: %d", errno);
213                 retValue = ioErr;
214         }
215     }
216
217     *dataLength = bytesSent;
218     return retValue;
219 }
220
221 static int st_validateServerCertificate (vlc_tls_t *session, const char *hostname) {
222
223     int result = -1;
224     vlc_tls_sys_t *sys = session->sys;
225     SecCertificateRef leaf_cert = NULL;
226
227     SecTrustRef trust = NULL;
228     OSStatus ret = SSLCopyPeerTrust(sys->p_context, &trust);
229     if (ret != noErr || trust == NULL) {
230         msg_Err(session, "error getting certifictate chain");
231         return -1;
232     }
233
234     CFStringRef cfHostname = CFStringCreateWithCString(kCFAllocatorDefault,
235                                                        hostname,
236                                                        kCFStringEncodingUTF8);
237
238
239     /* enable default root / anchor certificates */
240     ret = SecTrustSetAnchorCertificates(trust, NULL);
241     if (ret != noErr) {
242         msg_Err(session, "error setting anchor certificates");
243         result = -1;
244         goto out;
245     }
246
247     SecTrustResultType trust_eval_result = 0;
248
249     ret = SecTrustEvaluate(trust, &trust_eval_result);
250     if (ret != noErr) {
251         msg_Err(session, "error calling SecTrustEvaluate");
252         result = -1;
253         goto out;
254     }
255
256     switch (trust_eval_result) {
257         case kSecTrustResultUnspecified:
258         case kSecTrustResultProceed:
259             msg_Dbg(session, "cerfificate verification successful, result is %d", trust_eval_result);
260             result = 0;
261             goto out;
262
263         case kSecTrustResultRecoverableTrustFailure:
264         case kSecTrustResultDeny:
265         default:
266             msg_Warn(session, "cerfificate verification failed, result is %d", trust_eval_result);
267     }
268
269     /* get leaf certificate */
270     /* SSLCopyPeerCertificates is only available on OSX 10.5 or later */
271 #if !TARGET_OS_IPHONE
272     CFArrayRef cert_chain = NULL;
273     ret = SSLCopyPeerCertificates(sys->p_context, &cert_chain);
274     if (ret != noErr || !cert_chain) {
275         result = -1;
276         goto out;
277     }
278
279     if (CFArrayGetCount(cert_chain) == 0) {
280         CFRelease(cert_chain);
281         result = -1;
282         goto out;
283     }
284
285     leaf_cert = (SecCertificateRef)CFArrayGetValueAtIndex(cert_chain, 0);
286     CFRetain(leaf_cert);
287     CFRelease(cert_chain);
288 #else
289     /* SecTrustGetCertificateAtIndex is only available on 10.7 or iOS */
290     if (SecTrustGetCertificateCount(trust) == 0) {
291         result = -1;
292         goto out;
293     }
294
295     leaf_cert = SecTrustGetCertificateAtIndex(trust, 0);
296     CFRetain(leaf_cert);
297 #endif
298
299
300     /* check if leaf already accepted */
301     CFIndex max = CFArrayGetCount(sys->p_cred->whitelist);
302     for (CFIndex i = 0; i < max; ++i) {
303         CFDictionaryRef dict = CFArrayGetValueAtIndex(sys->p_cred->whitelist, i);
304         CFStringRef knownHost = (CFStringRef)CFDictionaryGetValue(dict, cfKeyHost);
305         SecCertificateRef knownCert = (SecCertificateRef)CFDictionaryGetValue(dict, cfKeyCertificate);
306
307         if (!knownHost || !knownCert)
308             continue;
309
310         if (CFEqual(knownHost, cfHostname) && CFEqual(knownCert, leaf_cert)) {
311             msg_Warn(session, "certificate already accepted, continuing");
312             result = 0;
313             goto out;
314         }
315     }
316
317     /* We do not show more certificate details yet because there is no proper API to get
318        a summary of the certificate. SecCertificateCopySubjectSummary is the only method
319        available on iOS and 10.6. More promising API functions such as
320        SecCertificateCopyLongDescription also print out the subject only, more or less.
321        But only showing the certificate subject is of no real help for the user.
322        We could use SecCertificateCopyValues, but then we need to parse all OID values for
323        ourself. This is too mad for just printing information the user will never check
324        anyway.
325      */
326
327     const char *msg = N_("You attempted to reach %s. "
328              "However the security certificate presented by the server "
329              "is unknown and could not be authenticated by any trusted "
330              "Certification Authority. "
331              "This problem may be caused by a configuration error "
332              "or an attempt to breach your security or your privacy.\n\n"
333              "If in doubt, abort now.\n");
334     int answer = dialog_Question(session, _("Insecure site"), vlc_gettext (msg),
335                                   _("Abort"), _("Accept certificate temporarily"), NULL, hostname);
336
337     if (answer == 2) {
338         msg_Warn(session, "Proceeding despite of failed certificate validation");
339
340         /* save leaf certificate in whitelist */
341         const void *keys[] = {cfKeyHost, cfKeyCertificate};
342         const void *values[] = {cfHostname, leaf_cert};
343         CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault,
344                                                    keys, values, 2,
345                                                    &kCFTypeDictionaryKeyCallBacks,
346                                                    &kCFTypeDictionaryValueCallBacks);
347         if (!dict) {
348             msg_Err(session, "error creating dict");
349             result = -1;
350             goto out;
351         }
352
353         CFArrayAppendValue(sys->p_cred->whitelist, dict);
354         CFRelease(dict);
355
356         result = 0;
357         goto out;
358
359     } else {
360         result = -1;
361         goto out;
362     }
363
364 out:
365     CFRelease(trust);
366
367     if (cfHostname)
368         CFRelease(cfHostname);
369     if (leaf_cert)
370         CFRelease(leaf_cert);
371
372     return result;
373 }
374
375 /*
376  * @return -1 on fatal error, 0 on successful handshake completion,
377  * 1 if more would-be blocking recv is needed,
378  * 2 if more would-be blocking send is required.
379  */
380 static int st_Handshake (vlc_tls_t *session, const char *host,
381                                         const char *service) {
382     VLC_UNUSED(service);
383
384     vlc_tls_sys_t *sys = session->sys;
385
386     OSStatus retValue = SSLHandshake(sys->p_context);
387
388     if (retValue == errSSLWouldBlock) {
389         msg_Dbg(session, "handshake is blocked, try again later");
390         return 1 + (sys->b_blocking_send ? 1 : 0);
391     }
392
393     switch (retValue) {
394         case noErr:
395             if (sys->b_server_mode == false && st_validateServerCertificate(session, host) != 0) {
396                 return -1;
397             }
398             msg_Dbg(session, "handshake completed successfully");
399             sys->b_handshaked = true;
400             return 0;
401
402         case errSSLServerAuthCompleted:
403             return st_Handshake(session, host, service);
404
405         case errSSLConnectionRefused:
406             msg_Err(session, "connection was refused");
407             return -1;
408         case errSSLNegotiation:
409             msg_Err(session, "cipher suite negotiation failed");
410             return -1;
411         case errSSLFatalAlert:
412             msg_Err(session, "fatal error occured during handshake");
413             return -1;
414
415         default:
416             msg_Err(session, "handshake returned error %d", (int)retValue);
417             return -1;
418     }
419 }
420
421 /**
422  * Sends data through a TLS session.
423  */
424 static int st_Send (void *opaque, const void *buf, size_t length)
425 {
426     vlc_tls_t *session = opaque;
427     vlc_tls_sys_t *sys = session->sys;
428     OSStatus ret = noErr;
429
430     /*
431      * SSLWrite does not return the number of bytes actually written to
432      * the socket, but the number of bytes written to the internal cache.
433      *
434      * If return value is errSSLWouldBlock, the underlying socket cannot
435      * send all data, but the data is already cached. In this situation,
436      * we need to call SSLWrite again. To ensure this call even for the
437      * last bytes, we return EAGAIN. On the next call, we give no new data
438      * to SSLWrite until the error is not errSSLWouldBlock anymore.
439      *
440      * This code is adapted the same way as done in curl.
441      * (https://github.com/bagder/curl/blob/master/lib/curl_darwinssl.c#L2067)
442      */
443
444     /* EAGAIN is not expected by net_Write in this situation,
445        so use EINTR here */
446     int againErr = sys->b_server_mode ? EAGAIN : EINTR;
447
448     size_t actualSize;
449     if (sys->i_send_buffered_bytes > 0) {
450         ret = SSLWrite(sys->p_context, NULL, 0, &actualSize);
451
452         if (ret == noErr) {
453             /* actualSize remains zero because no new data send */
454             actualSize = sys->i_send_buffered_bytes;
455             sys->i_send_buffered_bytes = 0;
456
457         } else if (ret == errSSLWouldBlock) {
458             errno = againErr;
459             return -1;
460         }
461
462     } else {
463         ret = SSLWrite(sys->p_context, buf, length, &actualSize);
464
465         if (ret == errSSLWouldBlock) {
466             sys->i_send_buffered_bytes = length;
467             errno = againErr;
468             return -1;
469         }
470     }
471
472     return ret != noErr ? st_Error(session, ret) : actualSize;
473 }
474
475 /**
476  * Receives data through a TLS session.
477  */
478 static int st_Recv (void *opaque, void *buf, size_t length)
479 {
480     vlc_tls_t *session = opaque;
481     vlc_tls_sys_t *sys = session->sys;
482
483     size_t actualSize;
484     OSStatus ret = SSLRead(sys->p_context, buf, length, &actualSize);
485
486     if (ret == errSSLWouldBlock && actualSize)
487         return actualSize;
488
489     /* peer performed shutdown */
490     if (ret == errSSLClosedNoNotify || ret == errSSLClosedGraceful) {
491         msg_Dbg(session, "Got close notification with code %d", ret);
492         return 0;
493     }
494
495     return ret != noErr ? st_Error(session, ret) : actualSize;
496 }
497
498 /**
499  * Closes a TLS session.
500  */
501 static void st_SessionClose (vlc_tls_creds_t *crd, vlc_tls_t *session) {
502
503     VLC_UNUSED(crd);
504
505     vlc_tls_sys_t *sys = session->sys;
506     msg_Dbg(session, "close TLS session");
507
508     if (sys->p_context) {
509         if (sys->b_handshaked) {
510             OSStatus ret = SSLClose(sys->p_context);
511             if (ret != noErr) {
512                 msg_Warn(session, "Cannot close ssl context");
513             }
514         }
515
516 #if TARGET_OS_IPHONE
517         CFRelease(sys->p_context);
518 #else
519         if (SSLDisposeContext(sys->p_context) != noErr) {
520             msg_Err(session, "error deleting context");
521         }
522 #endif
523     }
524     free(sys);
525 }
526
527 /**
528  * Initializes a client-side TLS session.
529  */
530
531 static int st_SessionOpenCommon (vlc_tls_creds_t *crd, vlc_tls_t *session,
532                                  int fd, bool b_server) {
533
534     vlc_tls_sys_t *sys = malloc(sizeof(*session->sys));
535     if (unlikely(sys == NULL))
536         return VLC_ENOMEM;
537
538     sys->p_cred = crd->sys;
539     sys->i_fd = fd;
540     sys->b_handshaked = false;
541     sys->b_blocking_send = false;
542     sys->i_send_buffered_bytes = 0;
543     sys->p_context = NULL;
544
545     session->sys = sys;
546     session->sock.p_sys = session;
547     session->sock.pf_send = st_Send;
548     session->sock.pf_recv = st_Recv;
549     session->handshake = st_Handshake;
550
551     SSLContextRef p_context = NULL;
552 #if TARGET_OS_IPHONE
553     p_context = SSLCreateContext(NULL, b_server ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
554     if (p_context == NULL) {
555         msg_Err(session, "cannot create ssl context");
556         return -1;
557     }
558 #else
559     if (SSLNewContext(b_server, &p_context) != noErr) {
560         msg_Err(session, "error calling SSLNewContext");
561         return -1;
562     }
563 #endif
564
565     sys->p_context = p_context;
566
567     OSStatus ret = SSLSetIOFuncs(p_context, st_SocketReadFunc, st_SocketWriteFunc);
568     if (ret != noErr) {
569         msg_Err(session, "cannot set io functions");
570         return -1;
571     }
572
573     ret = SSLSetConnection(p_context, session);
574     if (ret != noErr) {
575         msg_Err(session, "cannot set connection");
576         return -1;
577     }
578
579     return 0;
580 }
581
582 static int st_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
583                                      int fd, const char *hostname) {
584     msg_Dbg(session, "open TLS session for %s", hostname);
585
586     int ret = st_SessionOpenCommon(crd, session, fd, false);
587     if (ret != noErr) {
588         goto error;
589     }
590
591     vlc_tls_sys_t *sys = session->sys;
592     sys->b_server_mode = false;
593
594     ret = SSLSetPeerDomainName(sys->p_context, hostname, strlen(hostname));
595     if (ret != noErr) {
596         msg_Err(session, "cannot set peer domain name");
597         goto error;
598     }
599
600     /* disable automatic validation. We do so manually to also handle invalid
601        certificates */
602
603     /* this has effect only on iOS 5 and OSX 10.8 or later ... */
604     ret = SSLSetSessionOption(sys->p_context, kSSLSessionOptionBreakOnServerAuth, true);
605     if (ret != noErr) {
606         msg_Err (session, "cannot set session option");
607         goto error;
608     }
609 #if !TARGET_OS_IPHONE
610     /* ... thus calling this for earlier osx versions, which is not available on iOS in turn */
611     ret = SSLSetEnableCertVerify(sys->p_context, false);
612     if (ret != noErr) {
613         msg_Err(session, "error setting enable cert verify");
614         goto error;
615     }
616 #endif
617
618     return VLC_SUCCESS;
619
620 error:
621     st_SessionClose(crd, session);
622     return VLC_EGENERIC;
623 }
624
625 /**
626  * Initializes a client-side TLS credentials.
627  */
628 static int OpenClient (vlc_tls_creds_t *crd) {
629
630     msg_Dbg(crd, "open st client");
631
632     vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
633     if (unlikely(sys == NULL))
634         return VLC_ENOMEM;
635
636     sys->whitelist = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
637     sys->server_cert_chain = NULL;
638
639     crd->sys = sys;
640     crd->open = st_ClientSessionOpen;
641     crd->close = st_SessionClose;
642
643     return VLC_SUCCESS;
644 }
645
646 static void CloseClient (vlc_tls_creds_t *crd) {
647     msg_Dbg(crd, "close secure transport client");
648
649     vlc_tls_creds_sys_t *sys = crd->sys;
650
651     if (sys->whitelist)
652         CFRelease(sys->whitelist);
653
654     free(sys);
655 }
656
657 /* Begin of server-side methods */
658 #if !TARGET_OS_IPHONE
659
660 /**
661  * Initializes a server-side TLS session.
662  */
663 static int st_ServerSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
664                                  int fd, const char *hostname) {
665
666     VLC_UNUSED(hostname);
667     msg_Dbg(session, "open TLS server session");
668
669     int ret = st_SessionOpenCommon(crd, session, fd, true);
670     if (ret != noErr) {
671         goto error;
672     }
673
674     vlc_tls_sys_t *sys = session->sys;
675     sys->b_server_mode = true;
676
677     ret = SSLSetCertificate(sys->p_context, crd->sys->server_cert_chain);
678     if (ret != noErr) {
679         msg_Err(session, "cannot set server certificate");
680         goto error;
681     }
682
683     return VLC_SUCCESS;
684
685 error:
686     st_SessionClose(crd, session);
687     return VLC_EGENERIC;
688 }
689
690 /**
691  * Initializes server-side TLS credentials.
692  */
693 static int OpenServer (vlc_tls_creds_t *crd, const char *cert, const char *key) {
694
695     /*
696      * This function expects the label of the certificate in "cert", stored
697      * in the MacOS keychain. The appropriate private key is found automatically.
698      */
699     VLC_UNUSED(key);
700     OSStatus ret;
701
702     msg_Dbg(crd, "open st server");
703
704     vlc_tls_creds_sys_t *sys = malloc(sizeof(*sys));
705     if (unlikely(sys == NULL))
706         return VLC_ENOMEM;
707
708     /*
709      * Get the server certificate.
710      *
711      * This API is deprecated, but the replacement SecItemCopyMatching
712      * only works on >= 10.7
713      */
714     SecKeychainAttribute attrib = { kSecLabelItemAttr, strlen(cert), (void *)cert };
715     SecKeychainAttributeList attrList = { 1, &attrib };
716
717     SecKeychainSearchRef searchReference = NULL;
718     ret = SecKeychainSearchCreateFromAttributes(NULL, kSecCertificateItemClass,
719                                                  &attrList, &searchReference);
720     if (ret != noErr || searchReference == NULL) {
721         msg_Err(crd, "Cannot find certificate with alias %s", cert);
722         return VLC_EGENERIC;
723     }
724
725     SecKeychainItemRef itemRef = NULL;
726     ret = SecKeychainSearchCopyNext(searchReference, &itemRef);
727     if (ret != noErr) {
728         msg_Err(crd, "Cannot get certificate with alias %s, error: %d", cert, ret);
729         return VLC_EGENERIC;
730     }
731     CFRelease(searchReference);
732
733     /* cast allowed according to documentation */
734     SecCertificateRef certificate = (SecCertificateRef)itemRef;
735
736     SecIdentityRef cert_identity = NULL;
737     ret = SecIdentityCreateWithCertificate(NULL, certificate, &cert_identity);
738     if (ret != noErr) {
739         msg_Err(crd, "Cannot get private key for certificate");
740         CFRelease(certificate);
741         return VLC_EGENERIC;
742     }
743
744     /*
745      * We try to validate the server certificate, but do not care about the result.
746      * The only aim is to get the certificate chain.
747      */
748     SecPolicyRef policy = SecPolicyCreateSSL(true, NULL);
749     SecTrustRef trust_ref = NULL;
750     int result = VLC_SUCCESS;
751
752     /* According to docu its fine to pass just one certificate */
753     ret = SecTrustCreateWithCertificates((CFArrayRef)certificate, policy, &trust_ref);
754     if (ret != noErr) {
755         msg_Err(crd, "Cannot create trust");
756         result = VLC_EGENERIC;
757         goto out;
758     }
759
760     SecTrustResultType status;
761     ret = SecTrustEvaluate(trust_ref, &status);
762     if (ret != noErr) {
763         msg_Err(crd, "Error evaluating trust");
764         result = VLC_EGENERIC;
765         goto out;
766     }
767
768     CFArrayRef cert_chain = NULL;
769     CSSM_TP_APPLE_EVIDENCE_INFO *status_chain;
770     ret = SecTrustGetResult(trust_ref, &status, &cert_chain, &status_chain);
771     if (ret != noErr || !cert_chain) {
772         msg_Err(crd, "error while getting certificate chain");
773         result = VLC_EGENERIC;
774         goto out;
775     }
776
777     CFIndex num_cert_chain = CFArrayGetCount(cert_chain);
778
779     /* Build up the certificate chain array expected by SSLSetCertificate */
780     CFMutableArrayRef server_cert_chain = CFArrayCreateMutable(kCFAllocatorDefault, num_cert_chain, &kCFTypeArrayCallBacks);
781     CFArrayAppendValue(server_cert_chain, cert_identity);
782
783     msg_Dbg(crd, "Found certificate chain with %ld entries for server certificate", num_cert_chain);
784     if (num_cert_chain > 1)
785         CFArrayAppendArray(server_cert_chain, cert_chain, CFRangeMake(1, num_cert_chain - 1));
786     CFRelease(cert_chain);
787
788
789     sys->server_cert_chain = server_cert_chain;
790     sys->whitelist = NULL;
791
792     crd->sys = sys;
793     crd->open = st_ServerSessionOpen;
794     crd->close = st_SessionClose;
795
796 out:
797     if (policy)
798         CFRelease(policy);
799     if (trust_ref)
800         CFRelease(trust_ref);
801
802     if (certificate)
803         CFRelease(certificate);
804     if (cert_identity)
805         CFRelease(cert_identity);
806
807     return result;
808 }
809
810 static void CloseServer (vlc_tls_creds_t *crd) {
811     msg_Dbg(crd, "close secure transport server");
812
813     vlc_tls_creds_sys_t *sys = crd->sys;
814
815     if (sys->server_cert_chain)
816         CFRelease(sys->server_cert_chain);
817
818     free(sys);
819 }
820
821 #endif /* !TARGET_OS_IPHONE */