func convertSliceToCFArrayHelper(slice reflect.Value, helper func(reflect.Value) (cfTypeRef, error)) (C.CFArrayRef, error) { if slice.Len() == 0 { // short-circuit 0, so we can assume plists[0] is valid later return C.CFArrayCreate(nil, nil, 0, nil), nil } // assume slice is a slice/array, because our caller already checked plists := make([]cfTypeRef, slice.Len()) // defer the release defer func() { for _, cfObj := range plists { cfRelease(cfObj) } }() // convert the slice for i := 0; i < slice.Len(); i++ { cfType, err := helper(slice.Index(i)) if err != nil { return nil, err } plists[i] = cfType } // create the array callbacks := (*C.CFArrayCallBacks)(&C.kCFTypeArrayCallBacks) return C.CFArrayCreate(nil, (*unsafe.Pointer)(&plists[0]), C.CFIndex(len(plists)), callbacks), nil }
// ExportFromKeychain ... func ExportFromKeychain(itemRefsToExport []C.CFTypeRef, outputFilePath string, isAskForPassword bool) error { passphraseCString := C.CString("") defer C.free(unsafe.Pointer(passphraseCString)) var exportedData C.CFDataRef var exportParams C.SecItemImportExportKeyParameters exportParams.keyUsage = nil exportParams.keyAttributes = nil exportParams.version = C.SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION if isAskForPassword { exportParams.flags = C.kSecKeySecurePassphrase exportParams.passphrase = nil exportParams.alertTitle = nil promptText := C.CString("Enter a password which will be used to protect the exported items") defer C.free(unsafe.Pointer(promptText)) exportParams.alertPrompt = convertCStringToCFString(promptText) } else { exportParams.flags = 0 exportParams.passphrase = (C.CFTypeRef)(convertCStringToCFString(passphraseCString)) exportParams.alertTitle = nil exportParams.alertPrompt = nil } // create a C array from the input ptr := (*unsafe.Pointer)(&itemRefsToExport[0]) cfArrayForExport := C.CFArrayCreate( C.kCFAllocatorDefault, ptr, C.CFIndex(len(itemRefsToExport)), &C.kCFTypeArrayCallBacks) // do the export! status := C.SecItemExport(C.CFTypeRef(cfArrayForExport), C.kSecFormatPKCS12, 0, //C.kSecItemPemArmour, // Use kSecItemPemArmour to add PEM armour - the .p12 generated by Keychain Access.app does NOT have PEM armour &exportParams, &exportedData) if status != C.errSecSuccess { return fmt.Errorf("SecItemExport: error (OSStatus): %d", status) } // exportedData now contains your PKCS12 data // make sure it'll be released properly! defer C.CFRelease(C.CFTypeRef(exportedData)) dataBytes := convertCFDataRefToGoBytes(exportedData) if dataBytes == nil || len(dataBytes) < 1 { return errors.New("ExportFromKeychain: failed to convert export data - nil or empty") } if err := fileutil.WriteBytesToFile(outputFilePath, dataBytes); err != nil { return fmt.Errorf("ExportFromKeychain: failed to write into file: %s", err) } log.Debug("Export - success") return nil }
// ArrayToCFArray will return a CFArrayRef and if non-nil, must be released with // Release(ref). func ArrayToCFArray(a []C.CFTypeRef) C.CFArrayRef { var values []unsafe.Pointer for _, value := range a { values = append(values, unsafe.Pointer(value)) } numValues := len(values) var valuesPointer *unsafe.Pointer if numValues > 0 { valuesPointer = &values[0] } return C.CFArrayCreate(nil, valuesPointer, C.CFIndex(numValues), &C.kCFTypeArrayCallBacks) }
// Start creates a FSEventStream for the given path and schedules it with // global runloop. It's a nop if the stream was already started. func (s *stream) Start() error { if s.ref != nilstream { return nil } wg.Wait() p := C.CFStringCreateWithCStringNoCopy(nil, C.CString(s.path), C.kCFStringEncodingUTF8, nil) path := C.CFArrayCreate(nil, (*unsafe.Pointer)(unsafe.Pointer(&p)), 1, nil) ref := C.FSEventStreamCreate(nil, (C.FSEventStreamCallback)(C.gostream), &s.ctx, path, C.FSEventStreamEventId(atomic.LoadUint64(&since)), latency, flags) if ref == nilstream { return errCreate } C.FSEventStreamScheduleWithRunLoop(ref, runloop, C.kCFRunLoopDefaultMode) if C.FSEventStreamStart(ref) == C.Boolean(0) { C.FSEventStreamInvalidate(ref) return errStart } C.CFRunLoopWakeUp(runloop) s.ref = ref return nil }