/* * Copyright (c) 2001-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Modification History * * November 8, 2001 Dieter Siegmund * - created */ #include #include #include #include #include #include #include "EAPCertificateUtil.h" #include #include #include #include #include #include #include #include #include #include #include "Dialogue.h" #include "mylog.h" #include "myCFUtil.h" /** ** CredentialsDialogue **/ const CFStringRef kCredentialsDialogueSSID = CFSTR("SSID"); const CFStringRef kCredentialsDialogueAccountName = CFSTR("AccountName"); const CFStringRef kCredentialsDialoguePassword = CFSTR("Password"); const CFStringRef kCredentialsDialogueCertificates = CFSTR("Certificates"); const CFStringRef kCredentialsDialogueRememberInformation = CFSTR("RememberInformation"); struct CredentialsDialogue_s; #define LIST_HEAD_CredentialsDialogueHead LIST_HEAD(CredentialsDialogueHead, CredentialsDialogue_s) static LIST_HEAD_CredentialsDialogueHead S_CredentialsDialogueHead; static struct CredentialsDialogueHead * S_CredentialsDialogueHead_p = &S_CredentialsDialogueHead; #define LIST_ENTRY_CredentialsDialogue LIST_ENTRY(CredentialsDialogue_s) struct CredentialsDialogue_s { LIST_ENTRY_CredentialsDialogue entries; CFUserNotificationRef notif; CFRunLoopSourceRef rls; CredentialsDialogueResponseCallBack func; const void * arg1; const void * arg2; Boolean name_enabled; Boolean password_enabled; Boolean checkbox_enabled; CFArrayRef certificates; }; #define kEAPOLControllerPath "/System/Library/SystemConfiguration/EAPOLController.bundle" static CFBundleRef get_bundle(void) { static CFBundleRef bundle = NULL; CFURLRef url; if (bundle != NULL) { return (bundle); } url = CFURLCreateWithFileSystemPath(NULL, CFSTR(kEAPOLControllerPath), kCFURLPOSIXPathStyle, FALSE); if (url != NULL) { bundle = CFBundleCreate(NULL, url); CFRelease(url); } return (bundle); } static CFStringRef copy_localized_string(CFBundleRef bundle, CFStringRef ethernet_str, CFStringRef airport_str, CFTypeRef ssid) { CFStringRef str = NULL; if (ssid != NULL) { CFStringRef format; format = CFBundleCopyLocalizedString(bundle, airport_str, airport_str, NULL); if (format != NULL) { str = CFStringCreateWithFormat(NULL, NULL, format, ssid); CFRelease(format); } } else { str = CFBundleCopyLocalizedString(bundle, ethernet_str, ethernet_str, NULL); } if (str == NULL) { str = CFRetain(ethernet_str); } return (str); } static CFStringRef copy_localized_title(CFBundleRef bundle, CFTypeRef ssid) { #define kAirPort8021XTitleFormat CFSTR("Authenticating to network \"%@\"") #define kEthernet8021XTitle CFSTR("Authenticating to 802.1X network") return (copy_localized_string(bundle, kEthernet8021XTitle, kAirPort8021XTitleFormat, ssid)); } static CFArrayRef copy_certificate_labels(CredentialsDialogueRef dialogue_p, CFArrayRef certs, CFStringRef cert_label) { CFMutableArrayRef array = NULL; CFMutableArrayRef certs_filtered = NULL; int count; int i; CFRange r; count = CFArrayGetCount(certs); array = CFArrayCreateMutable(NULL, count + 1, &kCFTypeArrayCallBacks); /* add the first element which is reserved to mean no cert is selected */ CFArrayAppendValue(array, cert_label); r.location = 0; r.length = 1; certs_filtered = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks); for (i = 0; i < count; i++) { SecCertificateRef cert = NULL; SecIdentityRef identity; CFStringRef str = NULL; identity = (SecIdentityRef)CFArrayGetValueAtIndex(certs, i); SecIdentityCopyCertificate(identity, &cert); if (cert != NULL) { str = SecCertificateCopyShortDescription(NULL, cert, NULL); CFRelease(cert); } if (str != NULL) { int instance; CFStringRef new_str; for (instance = 2, new_str = CFRetain(str); CFArrayContainsValue(array, r, new_str); instance++) { CFRelease(new_str); new_str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%d)"), str, instance); } CFArrayAppendValue(array, new_str); r.length++; CFRelease(new_str); CFRelease(str); CFArrayAppendValue(certs_filtered, identity); } } dialogue_p->certificates = certs_filtered; return (array); } static CredentialsDialogueRef CredentialsDialogue_find(CFUserNotificationRef notif) { CredentialsDialogueRef scan; LIST_FOREACH(scan, S_CredentialsDialogueHead_p, entries) { if (scan->notif == notif) return (scan); } return (NULL); } static __inline__ CFOptionFlags S_CFUserNotificationResponse(CFOptionFlags flags) { return (flags & 0x3); } static void CredentialsDialogue_response(CFUserNotificationRef notif, CFOptionFlags response_flags) { int count; volatile CredentialsDialogueRef dialogue_p; int i; CredentialsDialogueResponse response; CFStringRef str; dialogue_p = CredentialsDialogue_find(notif); if (dialogue_p == NULL) { /* should not happen */ return; } bzero(&response, sizeof(response)); switch (S_CFUserNotificationResponse(response_flags)) { case kCFUserNotificationDefaultResponse: count = 0; if (dialogue_p->name_enabled) { count++; } if (dialogue_p->password_enabled) { count++; } for (i = 0; i < count; i++) { str = CFUserNotificationGetResponseValue(notif, kCFUserNotificationTextFieldValuesKey, i); if (str == NULL || CFStringGetLength(str) == 0) { continue; } if (i == 0 && dialogue_p->name_enabled) { response.username = CFRetain(str); } else { response.password = CFRetain(str); } } if (dialogue_p->checkbox_enabled) { if (response_flags & CFUserNotificationCheckBoxChecked(0)) { response.remember_information = TRUE; } } if (dialogue_p->certificates != NULL) { int which_cert; which_cert = (response_flags & CFUserNotificationPopUpSelection(-1)) >> 24; if (which_cert > 0) { response.chosen_identity = (SecIdentityRef) CFArrayGetValueAtIndex(dialogue_p->certificates, which_cert - 1); CFRetain(response.chosen_identity); } else if (dialogue_p->password_enabled == FALSE) { /* user did not choose a certificate, clear the username too */ my_CFRelease(&response.username); } } break; default: response.user_cancelled = TRUE; break; } if (dialogue_p->rls != NULL) { CFRunLoopSourceInvalidate(dialogue_p->rls); my_CFRelease(&dialogue_p->rls); } my_CFRelease(&dialogue_p->notif); my_CFRelease(&dialogue_p->certificates); (*dialogue_p->func)(dialogue_p->arg1, dialogue_p->arg2, &response); my_CFRelease(&response.username); my_CFRelease(&response.password); my_CFRelease(&response.chosen_identity); return; } #define kNetworkPrefPanePath "/System/Library/PreferencePanes/Network.prefPane" static CFURLRef copy_icon_url(CFStringRef icon) { CFBundleRef np_bundle; CFURLRef np_url; CFURLRef url = NULL; np_url = CFURLCreateWithFileSystemPath(NULL, CFSTR(kNetworkPrefPanePath), kCFURLPOSIXPathStyle, FALSE); if (np_url != NULL) { np_bundle = CFBundleCreate(NULL, np_url); if (np_bundle != NULL) { url = CFBundleCopyResourceURL(np_bundle, icon, CFSTR("icns"), NULL); if (url == NULL) { url = CFBundleCopyResourceURL(np_bundle, icon, CFSTR("tiff"), NULL); } CFRelease(np_bundle); } CFRelease(np_url); } return (url); } #define kTitleAirPortCertificate CFSTR("TitleAirPortCertificate") #define kTitleAirPortCertificateAndPassword CFSTR("TitleAirPortCertificateAndPassword") #define kTitleAirPortPassword CFSTR("TitleAirPortPassword") #define kTitleEthernetCertificate CFSTR("TitleEthernetCertificate") #define kTitleEthernetCertificateAndPassword CFSTR("TitleEthernetCertificateAndPassword") #define kTitleEthernetPassword CFSTR("TitleEthernetPassword") static CFUserNotificationRef CredentialsDialogueShow(CredentialsDialogueRef dialogue_p, CFBundleRef bundle, CFDictionaryRef details) { CFMutableArrayRef array = NULL; CFArrayRef certs; CFMutableDictionaryRef dict = NULL; SInt32 error = 0; CFOptionFlags flags = 0; CFStringRef icon; CFStringRef name; CFUserNotificationRef notif = NULL; CFStringRef password; CFIndex password_index = kCFNotFound; CFBooleanRef remember_information; CFTypeRef ssid; CFStringRef title; CFURLRef url; dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); url = CFBundleCopyBundleURL(bundle); if (url != NULL) { CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey, url); CFRelease(url); } ssid = CFDictionaryGetValue(details, kCredentialsDialogueSSID); icon = CFSTR("Network"); url = copy_icon_url(icon); if (url != NULL) { CFDictionarySetValue(dict, kCFUserNotificationIconURLKey, url); CFRelease(url); } /* button titles */ CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, CFSTR("Cancel")); CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, CFSTR("OK")); /* text field labels */ name = CFDictionaryGetValue(details, kCredentialsDialogueAccountName); password = CFDictionaryGetValue(details, kCredentialsDialoguePassword); if (name != NULL && isA_CFType(name, CFNullGetTypeID())) { /* don't put a name label */ } else { dialogue_p->name_enabled = TRUE; password_index = 1; } if (password != NULL && isA_CFType(password, CFNullGetTypeID())) { /* don't put a password label */ } else { if (password_index == kCFNotFound) { password_index = 0; } dialogue_p->password_enabled = TRUE; } /* text field values */ array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); if (dialogue_p->name_enabled) { if (name != NULL) { CFArrayAppendValue(array, name); } else if (password != NULL) { CFArrayAppendValue(array, CFSTR("")); } } if (dialogue_p->password_enabled) { flags |= CFUserNotificationSecureTextField(password_index); if (password != NULL) { CFArrayAppendValue(array, password); } } if (CFArrayGetCount(array) != 0) { CFDictionaryAddValue(dict, kCFUserNotificationTextFieldValuesKey, array); } my_CFRelease(&array); /* remember information checkbox */ remember_information = CFDictionaryGetValue(details, kCredentialsDialogueRememberInformation); if (remember_information != NULL) { dialogue_p->checkbox_enabled = TRUE; array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); CFArrayAppendValue(array, CFSTR("Remember information")); CFDictionarySetValue(dict, kCFUserNotificationCheckBoxTitlesKey, array); my_CFRelease(&array); if (CFBooleanGetValue(remember_information)) { flags |= CFUserNotificationCheckBoxChecked(0); } } /* certificate pop-up */ certs = CFDictionaryGetValue(details, kCredentialsDialogueCertificates); if (certs != NULL) { CFStringRef cert_label; CFArrayRef labels; cert_label = (dialogue_p->password_enabled) ? CFSTR("No certificate selected") : CFSTR("Select a certificate"); labels = copy_certificate_labels(dialogue_p, certs, cert_label); if (labels != NULL) { CFDictionarySetValue(dict, kCFUserNotificationPopUpTitlesKey, labels); CFRelease(labels); } } /* text field labels */ if (dialogue_p->name_enabled || dialogue_p->password_enabled) { array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); if (dialogue_p->name_enabled) { if (dialogue_p->password_enabled) { CFArrayAppendValue(array, CFSTR("User Name:")); } else { CFArrayAppendValue(array, CFSTR("Account Name (optional):")); } } if (dialogue_p->password_enabled) { CFArrayAppendValue(array, CFSTR("Password:")); } CFDictionaryAddValue(dict, kCFUserNotificationTextFieldTitlesKey, array); my_CFRelease(&array); } /* title */ if (certs != NULL && dialogue_p->password_enabled) { title = copy_localized_string(bundle, kTitleEthernetCertificateAndPassword, kTitleAirPortCertificateAndPassword, ssid); } else if (certs == NULL) { title = copy_localized_string(bundle, kTitleEthernetPassword, kTitleAirPortPassword, ssid); } else { title = copy_localized_string(bundle, kTitleEthernetCertificate, kTitleAirPortCertificate, ssid); } CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, title); CFRelease(title); notif = CFUserNotificationCreate(NULL, 0, flags, &error, dict); if (notif == NULL) { my_log(LOG_NOTICE, "CFUserNotificationCreate failed, %d", error); } my_CFRelease(&dict); return (notif); } CredentialsDialogueRef CredentialsDialogue_create(CredentialsDialogueResponseCallBack func, const void * arg1, const void * arg2, CFDictionaryRef details) { CFBundleRef bundle; CredentialsDialogueRef dialogue_p; CFUserNotificationRef notif = NULL; CFRunLoopSourceRef rls = NULL; bundle = get_bundle(); if (bundle == NULL) { my_log(LOG_NOTICE, "Can't get bundle"); return (NULL); } dialogue_p = malloc(sizeof(*dialogue_p)); if (dialogue_p == NULL) { my_log(LOG_NOTICE, "CredentialsDialogue_create: malloc failed"); return (NULL); } bzero(dialogue_p, sizeof(*dialogue_p)); notif = CredentialsDialogueShow(dialogue_p, bundle, details); if (notif == NULL) { goto failed; } rls = CFUserNotificationCreateRunLoopSource(NULL, notif, CredentialsDialogue_response, 0); if (rls == NULL) { my_log(LOG_NOTICE, "CFUserNotificationCreateRunLoopSource failed"); goto failed; } CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); dialogue_p->notif = notif; dialogue_p->rls = rls; dialogue_p->func = func; dialogue_p->arg1 = arg1; dialogue_p->arg2 = arg2; LIST_INSERT_HEAD(S_CredentialsDialogueHead_p, dialogue_p, entries); return (dialogue_p); failed: free(dialogue_p); my_CFRelease(¬if); my_CFRelease(&rls); return (NULL); } void CredentialsDialogue_free(CredentialsDialogueRef * dialogue_p_p) { CredentialsDialogueRef dialogue_p = *dialogue_p_p; if (dialogue_p) { LIST_REMOVE(dialogue_p, entries); if (dialogue_p->rls) { CFRunLoopSourceInvalidate(dialogue_p->rls); my_CFRelease(&dialogue_p->rls); } if (dialogue_p->notif) { (void)CFUserNotificationCancel(dialogue_p->notif); my_CFRelease(&dialogue_p->notif); } my_CFRelease(&dialogue_p->certificates); bzero(dialogue_p, sizeof(*dialogue_p)); free(dialogue_p); } *dialogue_p_p = NULL; return; } /** ** TrustDialogue **/ #include #include #include #include struct TrustDialogue_e; #define LIST_HEAD_TrustDialogueHead LIST_HEAD(TrustDialogueHead, TrustDialogue_s) static LIST_HEAD_TrustDialogueHead S_trust_head; static struct TrustDialogueHead * S_TrustDialogueHead_p = &S_trust_head; #define LIST_ENTRY_TrustDialogue LIST_ENTRY(TrustDialogue_s) struct TrustDialogue_s { LIST_ENTRY_TrustDialogue entries; TrustDialogueResponseCallBack func; const void * arg1; const void * arg2; pid_t pid; int fdp[2]; CFDictionaryRef trust_plist; }; static TrustDialogueRef TrustDialogue_find(pid_t pid) { TrustDialogueRef scan; LIST_FOREACH(scan, S_TrustDialogueHead_p, entries) { if (scan->pid == pid) return (scan); } return (NULL); } static void TrustDialogue_callback(pid_t pid, int status, __unused struct rusage * rusage, __unused void * context) { TrustDialogueRef dialogue_p; TrustDialogueResponse response; dialogue_p = TrustDialogue_find(pid); if (dialogue_p == NULL) { return; } response.proceed = FALSE; if (WIFEXITED(status)) { int exit_code = WEXITSTATUS(status); my_log(LOG_DEBUG, "TrustDialogue_callback: child %d exit(%d)", pid, exit_code); if (exit_code == 0) { response.proceed = 1; } } else if (WIFSIGNALED(status)) { my_log(LOG_DEBUG, "TrustDialogue_callback: child %d signaled(%d)", pid, WTERMSIG(status)); } dialogue_p->pid = -1; (*dialogue_p->func)(dialogue_p->arg1, dialogue_p->arg2, &response); return; } static void TrustDialogue_setup_child(TrustDialogueRef dialogue_p) { int fd; int i; /* close open FD's except for the read end of the pipe */ for (i = getdtablesize() - 1; i >= 0; i--) { if (i != dialogue_p->fdp[0]) { close(i); } } if (dialogue_p->fdp[0] != STDIN_FILENO) { dup(dialogue_p->fdp[0]); /* stdin */ close(dialogue_p->fdp[0]); } fd = open(_PATH_DEVNULL, O_RDWR, 0);/* stdout */ dup(fd); /* stderr */ return; } static void TrustDialogue_setup_parent(TrustDialogueRef dialogue_p) { size_t count; CFDataRef data; size_t write_count; close(dialogue_p->fdp[0]); /* close the read end */ dialogue_p->fdp[0] = -1; data = CFPropertyListCreateXMLData(NULL, dialogue_p->trust_plist); count = CFDataGetLength(data); /* disable SIGPIPE if it's currently enabled? XXX */ write_count = write(dialogue_p->fdp[1], (void *)CFDataGetBytePtr(data), count); /* enable SIGPIPE if it was enabled? XXX */ my_CFRelease(&data); if (write_count != count) { if (write_count == -1) { my_log(LOG_NOTICE, "TrustDialogue_setup_parent: write on pipe failed, %m"); } else { my_log(LOG_NOTICE, "TrustDialogue_setup_parent: wrote %d expected %d", write_count, count); } } close(dialogue_p->fdp[1]); /* close write end to deliver EOF to reader */ dialogue_p->fdp[1] = -1; return; } static void TrustDialogue_setup(pid_t pid, void * context) { TrustDialogueRef Dialogue_p = (TrustDialogueRef)context; if (pid == 0) { TrustDialogue_setup_child(Dialogue_p); } else { TrustDialogue_setup_parent(Dialogue_p); } return; } #define EAPTLSTRUST_PATH "/System/Library/PrivateFrameworks/EAP8021X.framework/Support/eaptlstrust.app/Contents/MacOS/eaptlstrust" static pthread_once_t initialized = PTHREAD_ONCE_INIT; TrustDialogueRef TrustDialogue_create(TrustDialogueResponseCallBack func, const void * arg1, const void * arg2, CFDictionaryRef trust_info, CFTypeRef ssid) { char * argv[2] = {EAPTLSTRUST_PATH, NULL}; CFBundleRef bundle; CFStringRef caller_label; CFMutableDictionaryRef dict; TrustDialogueRef dialogue_p; extern void _SCDPluginExecInit(); bundle = get_bundle(); if (bundle == NULL) { my_log(LOG_NOTICE, "Can't get bundle"); return (NULL); } pthread_once(&initialized, _SCDPluginExecInit); dialogue_p = (TrustDialogueRef)malloc(sizeof(*dialogue_p)); bzero(dialogue_p, sizeof(*dialogue_p)); dialogue_p->pid = -1; dialogue_p->fdp[0] = dialogue_p->fdp[1] = -1; if (pipe(dialogue_p->fdp) == -1) { my_log(LOG_NOTICE, "TrustDialogue_create: pipe failed, %m"); goto failed; } dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(dict, CFSTR("TrustInformation"), trust_info); CFDictionarySetValue(dict, CFSTR("Icon"), CFSTR("Network")); caller_label = copy_localized_title(bundle, ssid); if (caller_label != NULL) { CFDictionarySetValue(dict, CFSTR("CallerLabel"), caller_label); CFRelease(caller_label); } dialogue_p->trust_plist = CFDictionaryCreateCopy(NULL, dict); my_CFRelease(&dict); dialogue_p->pid = _SCDPluginExecCommand2(TrustDialogue_callback, NULL, geteuid(), getegid(), EAPTLSTRUST_PATH, argv, TrustDialogue_setup, dialogue_p); if (dialogue_p->pid == -1) { my_log(LOG_NOTICE, "TrustDialogue_create: _SCDPluginExecCommand2 failed, %m"); goto failed; } dialogue_p->func = func; dialogue_p->arg1 = arg1; dialogue_p->arg2 = arg2; LIST_INSERT_HEAD(S_TrustDialogueHead_p, dialogue_p, entries); return (dialogue_p); failed: TrustDialogue_free(&dialogue_p); return (NULL); } CFDictionaryRef TrustDialogue_trust_info(TrustDialogueRef dialogue_p) { if (dialogue_p->trust_plist == NULL) { return (NULL); } return (CFDictionaryGetValue(dialogue_p->trust_plist, CFSTR("TrustInformation"))); } void TrustDialogue_free(TrustDialogueRef * dialogue_p_p) { TrustDialogueRef dialogue_p; if (dialogue_p_p == NULL) { return; } dialogue_p = *dialogue_p_p; if (dialogue_p != NULL) { LIST_REMOVE(dialogue_p, entries); if (dialogue_p->pid != -1) { if (kill(dialogue_p->pid, SIGHUP)) { my_log(LOG_NOTICE, "TrustDialogue_free kill(%d) failed, %m", dialogue_p->pid); } } if (dialogue_p->fdp[0] != -1) { close(dialogue_p->fdp[0]); } if (dialogue_p->fdp[0] != -1) { close(dialogue_p->fdp[1]); } my_CFRelease(&dialogue_p->trust_plist); free(dialogue_p); *dialogue_p_p = NULL; } return; } /** ** AlertDialogue **/ struct AlertDialogue_s; #define LIST_HEAD_AlertDialogueHead LIST_HEAD(AlertDialogueHead, AlertDialogue_s) static LIST_HEAD_AlertDialogueHead S_AlertDialogueHead; static struct AlertDialogueHead * S_AlertDialogueHead_p = &S_AlertDialogueHead; #define LIST_ENTRY_AlertDialogue LIST_ENTRY(AlertDialogue_s) struct AlertDialogue_s { LIST_ENTRY_AlertDialogue entries; CFUserNotificationRef notif; CFRunLoopSourceRef rls; AlertDialogueResponseCallBack func; const void * arg1; const void * arg2; }; static CFUserNotificationRef AlertDialogueShow(AlertDialogueRef dialogue_p, CFBundleRef bundle, CFStringRef message) { CFMutableDictionaryRef dict = NULL; SInt32 error = 0; CFStringRef icon; CFUserNotificationRef notif = NULL; CFURLRef url; dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); url = CFBundleCopyBundleURL(bundle); if (url != NULL) { CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey, url); CFRelease(url); } icon = CFSTR("Network"); url = copy_icon_url(icon); if (url != NULL) { CFDictionarySetValue(dict, kCFUserNotificationIconURLKey, url); CFRelease(url); } CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, CFSTR("DISCONNECT")); CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, CFSTR("AUTHENTICATION_FAILED")); CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, message); notif = CFUserNotificationCreate(NULL, 0, 0, &error, dict); if (notif == NULL) { my_log(LOG_NOTICE, "CFUserNotificationCreate failed, %d", error); } my_CFRelease(&dict); return (notif); } static AlertDialogueRef AlertDialogue_find(CFUserNotificationRef notif) { AlertDialogueRef scan; LIST_FOREACH(scan, S_AlertDialogueHead_p, entries) { if (scan->notif == notif) return (scan); } return (NULL); } static void AlertDialogue_response(CFUserNotificationRef notif, CFOptionFlags response_flags) { volatile AlertDialogueRef dialogue_p; dialogue_p = AlertDialogue_find(notif); if (dialogue_p == NULL) { /* should not happen */ return; } if (dialogue_p->rls != NULL) { CFRunLoopSourceInvalidate(dialogue_p->rls); my_CFRelease(&dialogue_p->rls); } my_CFRelease(&dialogue_p->notif); (*dialogue_p->func)(dialogue_p->arg1, dialogue_p->arg2); return; } AlertDialogueRef AlertDialogue_create(AlertDialogueResponseCallBack func, const void * arg1, const void * arg2, CFStringRef message) { CFBundleRef bundle; AlertDialogueRef dialogue_p; CFUserNotificationRef notif = NULL; CFRunLoopSourceRef rls = NULL; bundle = get_bundle(); if (bundle == NULL) { my_log(LOG_NOTICE, "Can't get bundle"); return (NULL); } dialogue_p = malloc(sizeof(*dialogue_p)); if (dialogue_p == NULL) { my_log(LOG_NOTICE, "AlertDialogue_create: malloc failed"); return (NULL); } bzero(dialogue_p, sizeof(*dialogue_p)); notif = AlertDialogueShow(dialogue_p, bundle, message); if (notif == NULL) { goto failed; } rls = CFUserNotificationCreateRunLoopSource(NULL, notif, AlertDialogue_response, 0); if (rls == NULL) { my_log(LOG_NOTICE, "CFUserNotificationCreateRunLoopSource failed"); goto failed; } CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); dialogue_p->notif = notif; dialogue_p->rls = rls; dialogue_p->func = func; dialogue_p->arg1 = arg1; dialogue_p->arg2 = arg2; LIST_INSERT_HEAD(S_AlertDialogueHead_p, dialogue_p, entries); return (dialogue_p); failed: free(dialogue_p); my_CFRelease(¬if); my_CFRelease(&rls); return (NULL); } void AlertDialogue_free(AlertDialogueRef * dialogue_p_p) { AlertDialogueRef dialogue_p = *dialogue_p_p; if (dialogue_p) { LIST_REMOVE(dialogue_p, entries); if (dialogue_p->rls) { CFRunLoopSourceInvalidate(dialogue_p->rls); my_CFRelease(&dialogue_p->rls); } if (dialogue_p->notif) { (void)CFUserNotificationCancel(dialogue_p->notif); my_CFRelease(&dialogue_p->notif); } bzero(dialogue_p, sizeof(*dialogue_p)); free(dialogue_p); } *dialogue_p_p = NULL; return; } #ifdef TEST_DIALOGUE #include void my_callback(const void * arg1, const void * arg2, CredentialsDialogueResponseRef response) { CredentialsDialogueRef * dialogue_p_p = (CredentialsDialogueRef *)arg1; CredentialsDialogueRef temp = *dialogue_p_p; if (response->username) { SCPrint(TRUE, stdout, CFSTR("Account: %@\n"), response->username); } if (response->password) { SCPrint(TRUE, stdout, CFSTR("Password: %@\n"), response->password); } if (response->chosen_identity != NULL) { SecCertificateRef cert; CFStringRef str; SecIdentityCopyCertificate(response->chosen_identity, &cert); if (cert != NULL) { str = SecCertificateCopyShortDescription(NULL, cert, NULL); CFRelease(cert); } SCPrint(TRUE, stdout, CFSTR("Identity: %@\n"), str); CFRelease(str); } if (response->remember_information) { printf("Remember information checked\n"); } if (response->user_cancelled) { printf("User cancelled\n"); } CredentialsDialogue_free(&temp); return; } void my_alert_callback(const void * arg1, const void * arg2) { AlertDialogueRef alert_p; AlertDialogueRef * alert_p_p = (AlertDialogueRef *)arg1; if (alert_p_p == NULL) { printf("NULL pointer!\n"); return; } alert_p = *alert_p_p; AlertDialogue_free(alert_p_p); printf("Alert done\n"); return; } int main(int argc, char * argv[]) { AlertDialogueRef alert_p; AlertDialogueRef * alert_p_p = &alert_p; CredentialsDialogueRef dialogue_p; CredentialsDialogueRef dialogue_p2; CredentialsDialogueRef dialogue_p3; CFMutableDictionaryRef dict = NULL; CredentialsDialogueRef * p = &dialogue_p; CredentialsDialogueRef * p2 = &dialogue_p2; CredentialsDialogueRef * p3 = &dialogue_p3; CFArrayRef certs = NULL; (void)EAPSecIdentityListCreate(&certs); /* dialogue 1 */ dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(dict, kCredentialsDialogueAccountName, CFSTR("dieter")); /* disable the Password field */ CFDictionarySetValue(dict, kCredentialsDialoguePassword, kCFNull); CFDictionarySetValue(dict, kCredentialsDialogueSSID, CFSTR("SSID")); CFDictionarySetValue(dict, kCredentialsDialogueRememberInformation, kCFBooleanTrue); if (certs != NULL) { CFDictionarySetValue(dict, kCredentialsDialogueCertificates, certs); } dialogue_p = CredentialsDialogue_create(my_callback, p, NULL, dict); CFRelease(dict); /* dialogue 2 */ dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(dict, kCredentialsDialogueAccountName, CFSTR("dieter")); CFDictionarySetValue(dict, kCredentialsDialoguePassword, CFSTR("siegmund")); CFDictionarySetValue(dict, kCredentialsDialogueRememberInformation, kCFBooleanTrue); if (certs != NULL) { CFDictionarySetValue(dict, kCredentialsDialogueCertificates, certs); } dialogue_p2 = CredentialsDialogue_create(my_callback, p2, NULL, dict); CFRelease(dict); /* dialogue 3 */ dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(dict, kCredentialsDialogueAccountName, CFSTR("dieter")); CFDictionarySetValue(dict, kCredentialsDialoguePassword, CFSTR("siegmund")); CFDictionarySetValue(dict, kCredentialsDialogueSSID, CFSTR("SomeSSID")); CFDictionarySetValue(dict, kCredentialsDialogueRememberInformation, kCFBooleanFalse); dialogue_p3 = CredentialsDialogue_create(my_callback, p3, NULL, dict); alert_p = AlertDialogue_create(my_alert_callback, alert_p_p, NULL, CFSTR("Here we are")); CFRunLoopRun(); exit(0); return (0); } #endif /* TEST_DIALOGUE */