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