Accessing OS X keychain item from trusted application - objective-c

I'm creating a keychain and then I'm adding an item with predefined trusted aplication list to it:
SecKeychainCreate([keychainPath UTF8String], (UInt32)strlen(keychainPass), keychainPass, FALSE, NULL, &someKeychain);
OSStatus someStatus = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &list, len, encryptedPass, someKeychain, accessRef, &someKeychainItem);
When I open the newly created keychain with Keychain Access application, I can see my application on the trusted app list:
The problem is, when I try to read the key from that keychain through one of the trusted applications
SecKeychainUnlock(someKeychain, (UInt32)strlen(keychainPass), keychainPass, TRUE);
UInt32 passwordLen = 0;
void *passData = nil;
const char *cUser_name = [NSUserName() cStringUsingEncoding:NSUTF8StringEncoding];
OSStatus genericPassErr = SecKeychainFindGenericPassword(someKeychain, 0, NULL, strlen(cUser_name), cUser_name, &passwordLen, &passData, NULL);
genericPassErr equals -25293, which means
Error: 0xFFFF9D33 -25293 The user name or passphrase you entered is not correct.
Earlier in the code, I run SecKeychainSetUserInteractionAllowed(0), and if I comment this line out, I get prompted by the system for my permission for the application to access the keychain, and if I grant it, everything runs fine. However, the whole point is that I need to be able to do that without prompting the user. And I expect it to work like this since I added the app to the ACL. Do you know what am I doing wrong?
Everything works without prompt as well when I tick the "all programs have access to this item" radio box in the attached screenshot. But I don't want everyone to be able to access it, just the listed apps.

I was able to make a similar test program work. However, I had to remove and re-add the tool to the always allowed list after each time I had rebuilt it. I did get the same error code when not doing that.
Here's the code:
#import <Foundation/Foundation.h>
#import <Security/Security.h>
int main()
{
#autoreleasepool
{
SecKeychainRef kc;
OSStatus status = SecKeychainSetUserInteractionAllowed(false);
printf("status: %d\n", status);
status = SecKeychainOpen("/Users/tsnorri/Library/Keychains/test.keychain", &kc);
printf("status: %d\n", status);
{
char const *keychainPass = "test123";
status = SecKeychainUnlock(kc, (UInt32) strlen(keychainPass), keychainPass, true);
CFStringRef err = SecCopyErrorMessageString(status, NULL);
printf("status: %d err: %s\n", status, [(id) err UTF8String]);
CFRelease(err);
}
UInt32 passwordLen = 0;
void *passData = NULL;
char const *userName = "tsnorri";
char const *serviceName = "test";
{
SecKeychainItemRef item = NULL;
status = SecKeychainFindGenericPassword(kc, strlen(serviceName), serviceName, strlen(userName), userName, &passwordLen, &passData, &item);
CFStringRef err = SecCopyErrorMessageString(status, NULL);
printf("status: %d err: %s\n", status, [(id) err UTF8String]);
CFRelease(err);
}
printf("pass: %s\n", passData);
}
return 0;
}

Related

KDC sever not found in kerberos database

I have kerberos installed with a kdc server and a client. They both work and I can generate ticket without any problem via kinit. However, I cannot do that via the MIT Kerberos API. I have the following code, which should generate a ticket with the kdc:
#include <stdio.h>
#include <krb5.h>
#include <com_err.h>
#include <string.h>
#include <unistd.h>
#define TKT_LIFETIME 30
#define NO_REPLAYCACHE
static void syslog_err(const char* tag, long code, const char*format, va_list args){
printf("%s: %s in %s\n", tag, error_message(code), format);
}
#define check_code(err, tag)\
if(err){\
void (*proc)(const char*, long, const char*, va_list);\
proc=set_com_err_hook(syslog_err);\
com_err("Error: ",(err),(tag));\
(void)set_com_err_hook(proc);\
goto cleanup;\
}
int main() {
const char *username="foo#DOMAIN.COM";
const char *password="pass";
//const char *host="";
const char kt_pathname[256] = "/etc/krb5.keytab";
const char service[256]="krbtgt/DOMAIN.COM";
const char host[256]="DOMAIN.COM";
krb5_error_code err;
krb5_context context;
krb5_auth_context auth_context = NULL;
krb5_creds credentials;
krb5_principal user_principal,
service_principal;
krb5_keytab keytab=NULL;
krb5_ccache ccache;
char ccache_name[L_tmpnam + 8];
krb5_get_init_creds_opt * gic_options;
#ifndef NO_REPLAYCACHE
krb5_verify_init_creds_opt vic_options;
#endif
krb5_data apreq_pkt;
char myhostname[256],sprinc[256];
int have_user_principal =0,
have_service_principal=0,
have_keytab=0,
have_credentials=0,
success=-1;
apreq_pkt.data=NULL;
/* --------------------------------------------------------------------------------- */
err = krb5_init_context(&context);
check_code(err, "init context");
(void) memset(ccache_name,0,sizeof(ccache_name));
(void) strcpy(ccache_name, "MEMORY:");
(void) mkstemp(&ccache_name[7]);
err = krb5_cc_resolve(context,ccache_name,&ccache);
/* else:
* err = krb5_cc_default(context,&ccache);
*/
if(err != 0) printf("[*] Error resolving credential cache name. Code: %d\n", err);
err = krb5_parse_name(context, username, &user_principal);
if(err != 0) printf("[*] Error parsing kerberos name. Code: %d\n", err);
else have_user_principal=1;
err = krb5_cc_initialize(context,ccache,user_principal);
if(err != 0) printf("[*] Error initializing credential cache. Code: %d\n", err);
(void) memset( (char*)&credentials, 0, sizeof(credentials));
// TODO: swtich later to: (void) gethostname(myhostname, sizeof(myhostname));
// TODO: not sure if double pointer.
krb5_get_init_creds_opt_alloc(context, &gic_options); // Allocate a new initial credential options structure
krb5_get_init_creds_opt_set_tkt_life(gic_options, TKT_LIFETIME); // Set the ticket lifetime in initial credential options
err = krb5_get_init_creds_password(context, &credentials, user_principal,password,0,0,0,NULL,gic_options); //Get initial credentials using a password
switch(err){
case 0:
have_credentials=1;
printf("[*] Successfully received credentials\n");
break;
case KRB5KDC_ERR_PREAUTH_FAILED:
printf("[*] Generic Pre-athentication failure. Unable to login. Access denied.\n");
case KRB5KRB_AP_ERR_BAD_INTEGRITY:
printf("[*] Decrypt integrity check failed - no domain controller\n");
case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
printf("[*] Bad username or password\n");
case KRB5_KDC_UNREACH:
printf("[*] Cannot contact any KDC for requested realm\n");
case KRB5_LIBOS_PWDINTR:
printf("[*] Password read interrupted\n");
case KRB5_REALM_CANT_RESOLVE:
printf("[*] Cannot resolve network address for KDC in requested realm\n");
case KRB5KDC_ERR_KEY_EXP:
printf("[*] Password has expired\n");
case KRB5_LIBOS_BADPWDMATCH:
printf("[*] Password mismatch\n");
case KRB5_CHPW_PWDNULL:
printf("[*] New password cannot be zero length\n");
case KRB5_CHPW_FAIL:
printf("[*] Password change failed\n");
default:
success=0;
break;
}
err = krb5_cc_store_cred(context, ccache, &credentials); // Store credentials in a credential cache
if (err != 0)
printf("[*] Failed storing credentials in credential cache\n");
char ***realmsp=malloc(sizeof(***realmsp));
err = krb5_get_host_realm(context, NULL,realmsp);
check_code(err, "krb5_get_host_realm");
free(realmsp);
err = krb5_sname_to_principal(context,host,service,KRB5_NT_SRV_HST,&service_principal); //Generate a full principal name from a service name KRB5_NT_UNKNOWN
check_code(err, "sname_to_principal");
have_service_principal=1;
err = krb5_kt_resolve(context,kt_pathname,&keytab); //Get a handle for a key table
check_code(err, "kt_resolve");
have_keytab=1;
/*
* A replay cache keeps track of all authenticators recenntly presented to a service. If a duplicate authneitcation request is detected in the replay cache,
* an error message is sent to application program
*/
#ifndef NO_REPLAYCACHE
krb5_verify_init_creds_opt_init(&vic_options); //Initialize a credential verification options structure
krb5_verify_init_creds_opt_set_ap_req_nofail(&vic_options,1); //Set whether credential verification is required
err = krb5_verify_init_creds(context, &credentials, service_principal, keytab,0,&vic_options); //Verify initial credentials against a keytab
check_code(err,"verify init credentials");
#else
//**********************************************CODE FAILES IN THE FOLLOWING FUNCTION **********************
err = krb5_mk_req(context,&auth_context,0,service,host,NULL,ccache,&apreq_pkt);
if(auth_context){
krb5_auth_con_free(context,auth_context); //Free a krb5_auth_context structure.
auth_context=NULL;
}
err = krb5_auth_con_init(context,&auth_context); //Create and initialize an authentication context
if (err!=0)
printf("[*] Failed to initialize an authentication context\n");
err = krb5_auth_con_setflags(context,auth_context,~KRB5_AUTH_CONTEXT_DO_TIME); //Set a flags field in a krb5_auth_context structure
if (err!=0)
printf("[*] Failed to set flags in context structure\n");
err = krb5_rd_req(context,&auth_context,&apreq_pkt,service_principal,keytab,NULL,NULL); // This function parses, decrypts and verifies a AP-REQ message
if (err!=0)
printf("[*] Failed to decrypt/parse AP-REQ message \n");
if(auth_context){
krb5_auth_con_free(context,auth_context); //Free a krb5_auth_context structure.
auth_context=NULL;
}
#endif
err = krb5_cc_destroy(context,ccache);
if (err!=0)
printf("[*] Failed to destroy kerberos context \n");
else
success=1;
cleanup:
if(apreq_pkt.data)
krb5_free_data_contents(context,&apreq_pkt); // Free the contents of a krb5_data structure and zero the data field
if(have_keytab){
krb5_kt_close(context,keytab);
if (err!=0)
printf("[*] Failed to destroy the keytab \n"); //Close a key table handle
}
if(have_user_principal)
krb5_free_principal(context,user_principal);
if(have_service_principal)
krb5_free_principal(context,service_principal);
if(have_credentials)
krb5_free_cred_contents(context,&credentials);
if(context)
krb5_free_context(context);
return 0;
}
when the code reaches the "krb5_mk_req" function, it fails with the error: "server not found in Kerberos database". I tried many combination with service + host, non of them worked. In kinit, I can generate a valid ticket with "kinit -S krbtgt#DOMAIN.COM". What am I missing?
(DOMAIN.COM is not my real domain)
Thank you very much!
Seems either you have to create database if it is not already available otherwise you have to configure it in kerberos configuration file. Please check below link which explains how to create or configure kdc database.
https://www.thetechnojournals.com/2019/12/setting-up-kerberos-in-mac-os-x.html

in windows 10, redirect port monitor (redmon), run as user doesn't work

I have a redirected printer port that use redmon (redirect port monitor) with a postscript printer driver to convert postscript to pdf and apply some other effects like watermarks, overlays, etc.
In win 7 all work fine but in windows 10 the process run under system user account.
In the configuration window of the printer port there is a flag called "Run as user" and in win7, checking this flag let the job running under the user account.
In Windows 10 it seems not working.
Any suggestion will be very appreciated.
Thank you.
Roy
I had a similar problem. I needed the user that printed the document to select the type of document and a patient ID. Then print the document to our EHR system as a PDF. Works in Windows 7 when "Run as User" is checked, but not on Windows 10. Redmon always runs the program as "SYSTEM". So I added a bit to the beginning of the program to check the user name. If it is "SYSTEM" the program looks for the an interactive user on the system by finding an instance of explorer.exe. If more than one interactive user is logged onto the system this will fail. Not a problem for my task. The program then starts another instance of itself running as the same user as explorer.exe, passing the same command line. A pipe is used so that stdin from the first instance can be piped to stdin on the second instance. Another limitation is that on a 64 bit OS, a 64 bit version of the program must be used. Otherwise explorer.exe may not be found.
The following code is what I placed at the beginning of my program. Don't be fooled by the program starting at main(). I am using a GUII toolkit that has WinMain() in it and then calls main(). I have only tested the code on ASCII programs. I tried to use the ASCII version of calls so that it would work with non-ASCII programs, but I am not sure I got all of them.
The LogInfoSys("Hello World"); function just writes to a log file.
Good luck.
#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <time.h>
#include <direct.h>
#include <process.h>
#include <sqlext.h>
#include <Psapi.h>
#include <tlhelp32.h>
int main(int argc, char *argv[])
{
int error;
char msg[1024];
DWORD *processIDs;
int processCount;
HANDLE hProcess = NULL;
HANDLE hToken;
char userName[64];
char progName[1024];
int i, j;
char nameMe[256];
char domainMe[256];
PTOKEN_USER ptuMe = NULL;
PROCESS_INFORMATION procInfo;
STARTUPINFO startUpInfo;
HMODULE *hMod;
DWORD cbNeeded;
SECURITY_ATTRIBUTES saAttr;
HANDLE hChildStd_IN_Rd = NULL;
HANDLE hChildStd_IN_Wr = NULL;
i = 64; // Get user name, if it is "SYSTEM" redirect input to output to a new instance of the program
GetUserNameA(userName, &i);
if (_stricmp(userName, "system") == 0)
{
LogInfoSys("Running as SYSTEM");
processIDs = (DWORD *)calloc(16384, sizeof(DWORD)); // Look for explorer.exe running. If found that should be the user we want to run as.
EnumProcesses(processIDs, sizeof(DWORD) * 16384, &i); // If there is more than one that is OK as long as they are both being run by the same
processCount = i / sizeof(DWORD); // user. If more than one user is logged on, this will be a problem.
hMod = (HMODULE *)calloc(4096, sizeof(HMODULE));
hProcess = NULL;
for (i = 0; (i < processCount) && (hProcess == NULL); i++)
{
if (processIDs[i] == 11276)
Sleep(0);
if (processIDs[i] != 0)
{
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIDs[i]);
if (hProcess != NULL)
{
cbNeeded = 0;
error = EnumProcessModules(hProcess, hMod, sizeof(HMODULE) * 4096, &cbNeeded);
if (error == 0)
{
error = GetLastError();
Sleep(0);
}
progName[0] = 0;
error = GetModuleBaseNameA(hProcess, hMod[0], progName, 1024);
if (error == 0)
{
error = GetLastError();
Sleep(0);
}
if (_stricmp(progName, "explorer.exe") != 0)
{
CloseHandle(hProcess);
hProcess = NULL;
}
else
{
LogInfoSys("Found explorer.exe");
}
}
}
}
LogInfoSys("After looking for processes.");
nameMe[0] = domainMe[0] = 0;
if (hProcess != NULL)
{
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&hChildStd_IN_Rd, &hChildStd_IN_Wr, &saAttr, 0)) // Create a pipe for the child process's STDIN.
LogInfoSys("Stdin CreatePipe error");
if (!SetHandleInformation(hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) // Ensure the write handle to the pipe for STDIN is not inherited.
LogInfoSys("Stdin SetHandleInformation errir");
if (OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken) != 0)
{
GetStartupInfo(&startUpInfo);
startUpInfo.cb = sizeof(STARTUPINFO);
startUpInfo.lpReserved = NULL;
startUpInfo.lpDesktop = NULL;
startUpInfo.lpTitle = NULL;
startUpInfo.dwX = startUpInfo.dwY = 0;
startUpInfo.dwXSize = 0;
startUpInfo.dwYSize = 0;
startUpInfo.dwXCountChars = 0;
startUpInfo.dwYCountChars = 0;
startUpInfo.dwFillAttribute = 0;
startUpInfo.dwFlags |= STARTF_USESTDHANDLES;
startUpInfo.wShowWindow = 0;
startUpInfo.cbReserved2 = 0;
startUpInfo.lpReserved = NULL;
startUpInfo.hStdInput = hChildStd_IN_Rd;
startUpInfo.hStdOutput = NULL;
startUpInfo.hStdError = NULL;
GetModuleFileName(NULL, progName, 1024);
i = CreateProcessAsUserA(hToken, progName, GetCommandLine(), NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startUpInfo, &procInfo);
if (i == 0)
{
i = GetLastError();
}
do
{
i = (int)fread(msg, 1, 1024, stdin);
if (i > 0)
WriteFile(hChildStd_IN_Wr, msg, i, &j, NULL);
} while (i > 0);
}
}
LogInfoSys("End of running as SYSTEM.");
exit(0);
}
/**********************************************************************************************************
*
* End of running as SYSTEM and start of running as the user that printed the document (I hope).
*
**********************************************************************************************************/
exit(0);
}

CoreAudio AudioQueue callback function never called, no errors reported

I am trying to do a simple playback from a file functionality and it appears that my callback function is never called. It doesn't really make sense because all of the OSStatuses come back 0 and other numbers all appear correct as well (like the output packets read pointer from AudioFileReadPackets).
Here is the setup:
OSStatus stat;
stat = AudioFileOpenURL(
(CFURLRef)urlpath, kAudioFileReadPermission, 0, &aStreamData->aFile
);
UInt32 dsze = 0;
stat = AudioFileGetPropertyInfo(
aStreamData->aFile, kAudioFilePropertyDataFormat, &dsze, 0
);
stat = AudioFileGetProperty(
aStreamData->aFile, kAudioFilePropertyDataFormat, &dsze, &aStreamData->aDescription
);
stat = AudioQueueNewOutput(
&aStreamData->aDescription, bufferCallback, aStreamData, NULL, NULL, 0, &aStreamData->aQueue
);
aStreamData->pOffset = 0;
for(int i = 0; i < NUM_BUFFERS; i++) {
stat = AudioQueueAllocateBuffer(
aStreamData->aQueue, aStreamData->aDescription.mBytesPerPacket, &aStreamData->aBuffer[i]
);
bufferCallback(aStreamData, aStreamData->aQueue, aStreamData->aBuffer[i]);
}
stat = AudioQueuePrime(aStreamData->aQueue, 0, NULL);
stat = AudioQueueStart(aStreamData->aQueue, NULL);
(Not shown is where I'm checking the value of stat in between the functions, it just comes back normal.)
And the callback function:
void bufferCallback(void *uData, AudioQueueRef queue, AudioQueueBufferRef buffer) {
UInt32 bread = 0;
UInt32 pread = buffer->mAudioDataBytesCapacity / player->aStreamData->aDescription.mBytesPerPacket;
OSStatus stat;
stat = AudioFileReadPackets(
player->aStreamData->aFile, false, &bread, NULL, player->aStreamData->pOffset, &pread, buffer->mAudioData
);
buffer->mAudioDataByteSize = bread;
stat = AudioQueueEnqueueBuffer(queue, buffer, 0, NULL);
player->aStreamData->pOffset += pread;
}
Where aStreamData is my user data struct (typedefed so I can use it as a class property) and player is a static instance of the controlling Objective-C class. If any other code is wanted please let me know. I am a bit at my wit's end. Printing any of the numbers involved here yields the correct result, including functions in bufferCallback when I call it myself in the allocate loop. It just never gets called thereafter. The start up method returns and nothing happens.
Also anecdotally, I am using a peripheral device (an MBox Pro 3) to play the sound which CoreAudio only boots up when it is about to output. IE if I start iTunes or something, the speakers pop faintly and there is an LED that goes from blinking to solid. The device boots up like it does so CA is definitely doing something. (Also I've of course tried it with the onboard Macbook sound sans the device.)
I've read other solutions to problems that sound similiar and they don't work. Stuff like using multiple buffers which I am doing now and doesn't appear to make any difference.
I basically assume I am doing something obviously wrong somehow but not sure what it could be. I've read the relevant documentation, looked at the available code examples and scoured the net a bit for answers and it appears that this is all I need to do and it should just go.
At the very least, is there anything else I can do to investigate?
My first answer was not good enough, so I compiled a minimal example that will play a 2 channel, 16 bit wave file.
The main difference from your code is that I made a property listener listening for play start and stop events.
As for your code, it seems legit at first glance. Two things I will point out, though:
1. Is seems you are allocating buffers with TOO SMALL a buffer size. I have noticed that AudioQueues won't play if the buffers are too small, which seems to fit your problem.
2. Have you verified the properties returned?
Back to my code example:
Everything is hard coded, so it is not exactly good coding practice, but it shows how you can do it.
AudioStreamTest.h
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
uint32_t bufferSizeInSamples;
AudioFileID file;
UInt32 currentPacket;
AudioQueueRef audioQueue;
AudioQueueBufferRef buffer[3];
AudioStreamBasicDescription audioStreamBasicDescription;
#interface AudioStreamTest : NSObject
- (void)start;
- (void)stop;
#end
AudioStreamTest.m
#import "AudioStreamTest.h"
#implementation AudioStreamTest
- (id)init
{
self = [super init];
if (self) {
bufferSizeInSamples = 441;
file = NULL;
currentPacket = 0;
audioStreamBasicDescription.mBitsPerChannel = 16;
audioStreamBasicDescription.mBytesPerFrame = 4;
audioStreamBasicDescription.mBytesPerPacket = 4;
audioStreamBasicDescription.mChannelsPerFrame = 2;
audioStreamBasicDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM;
audioStreamBasicDescription.mFramesPerPacket = 1;
audioStreamBasicDescription.mReserved = 0;
audioStreamBasicDescription.mSampleRate = 44100;
}
return self;
}
- (void)start {
AudioQueueNewOutput(&audioStreamBasicDescription, AudioEngineOutputBufferCallback, (__bridge void *)(self), NULL, NULL, 0, &audioQueue);
AudioQueueAddPropertyListener(audioQueue, kAudioQueueProperty_IsRunning, AudioEnginePropertyListenerProc, NULL);
AudioQueueStart(audioQueue, NULL);
}
- (void)stop {
AudioQueueStop(audioQueue, YES);
AudioQueueRemovePropertyListener(audioQueue, kAudioQueueProperty_IsRunning, AudioEnginePropertyListenerProc, NULL);
}
void AudioEngineOutputBufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
if (file == NULL) return;
UInt32 bytesRead = bufferSizeInSamples * 4;
UInt32 packetsRead = bufferSizeInSamples;
AudioFileReadPacketData(file, false, &bytesRead, NULL, currentPacket, &packetsRead, inBuffer->mAudioData);
inBuffer->mAudioDataByteSize = bytesRead;
currentPacket += packetsRead;
if (bytesRead == 0) {
AudioQueueStop(inAQ, false);
}
else {
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
}
}
void AudioEnginePropertyListenerProc (void *inUserData, AudioQueueRef inAQ, AudioQueuePropertyID inID) {
//We are only interested in the property kAudioQueueProperty_IsRunning
if (inID != kAudioQueueProperty_IsRunning) return;
//Get the status of the property
UInt32 isRunning = false;
UInt32 size = sizeof(isRunning);
AudioQueueGetProperty(inAQ, kAudioQueueProperty_IsRunning, &isRunning, &size);
if (isRunning) {
currentPacket = 0;
NSString *fileName = #"/Users/roy/Documents/XCodeProjectsData/FUZZ/03.wav";
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: fileName];
AudioFileOpenURL((__bridge CFURLRef) fileURL, kAudioFileReadPermission, 0, &file);
for (int i = 0; i < 3; i++){
AudioQueueAllocateBuffer(audioQueue, bufferSizeInSamples * 4, &buffer[i]);
UInt32 bytesRead = bufferSizeInSamples * 4;
UInt32 packetsRead = bufferSizeInSamples;
AudioFileReadPacketData(file, false, &bytesRead, NULL, currentPacket, &packetsRead, buffer[i]->mAudioData);
buffer[i]->mAudioDataByteSize = bytesRead;
currentPacket += packetsRead;
AudioQueueEnqueueBuffer(audioQueue, buffer[i], 0, NULL);
}
}
else {
if (file != NULL) {
AudioFileClose(file);
file = NULL;
for (int i = 0; i < 3; i++) {
AudioQueueFreeBuffer(audioQueue, buffer[i]);
buffer[i] = NULL;
}
}
}
}
-(void)dealloc {
[super dealloc];
AudioQueueDispose(audioQueue, true);
audioQueue = NULL;
}
#end
Lastly, I want to include some research I have done today to test the robustness of AudioQueues.
I have noticed that if you make too small AudioQueue buffers, it won't play at all. That made me play around a bit to see why it is not playing.
If I try buffer size that can hold only 150 samples, I get no sound at all.
If I try buffer size that can hold 175 samples, it plays the whole song through, but with A lot of distortion. 175 amounts to a tad less than 4 ms of audio.
AudioQueue keeps asking for new buffers as long as you keep supplying buffers. That is regardless of AudioQueue actually playing your buffers or not.
If you supply a buffer with size 0, the buffer will be lost and an error kAudioQueueErr_BufferEmpty is returned for that queue enqueue request. You will never see AudioQueue ask you to fill that buffer again. If this happened for the last queue you have posted, AudioQueue will stop asking you to fill any more buffers. In that case you will not hear any more audio for that session.
To see why AudioQueues is not playing anything with smaller buffer sizes, I made a test to see if my callback is called at all even when there is no sound. The answer is that the buffers gets called all the time as long as AudioQueues is playing and needs data.
So if you keep feeding buffers to the queue, no buffer is ever lost. It doesn't happen. Unless there is an error, of course.
So why is no sound playing?
I tested to see if 'AudioQueueEnqueueBuffer()' returned any errors. It did not. No other errors within my play routine either. The data returned from reading from file is also good.
Everything is normal, buffers are good, data re-enqueued is good, there is just no sound.
So my last test was to slowly increase buffer size till I could hear anything. I finally heard faint and sporadic distortion.
Then it came to me...
It seems that the problem lies with that the system tries to keep the stream in sync with time so if you enqueue audio, and the time for the audio you wanted to play has passed, it will just skip that part of the buffer. If the buffer size becomes too small, more and more data is dropped or skipped until the audio system is in sync again. Which is never if the buffer size is too small. (You can hear this as distortion if you chose a buffer size that is barely large enough to support continuous play.)
If you think about it, it is the only way the audio queue can work, but it is a good realisation when you are clueless like me and "discover" how it really works.
I decided to take a look at this again and was able to solve it by making the buffers larger. I've accepted the answer by #RoyGal since it was their suggestion but I wanted to provide the actual code that works since I guess others are having the same problem (question has a few favorites that aren't me at the moment).
One thing I tried was making the packet size larger:
aData->aDescription.mFramesPerPacket = 512; // or some other number
aData->aDescription.mBytesPerPacket = (
aData->aDescription.mFramesPerPacket * aData->aDescription.mBytesPerFrame
);
This does NOT work: it causes AudioQueuePrime to fail with an AudioConverterNew returned -50 message. I guess it wants mFramesPerPacket to be 1 for PCM.
(I also tried setting the kAudioQueueProperty_DecodeBufferSizeFrames property which didn't seem to do anything. Not sure what it's for.)
The solution seems to be to only allocate the buffer(s) with the specified size:
AudioQueueAllocateBuffer(
aData->aQueue,
aData->aDescription.mBytesPerPacket * N_BUFFER_PACKETS / N_BUFFERS,
&aData->aBuffer[i]
);
And the size has to be sufficiently large. I found the magic number is:
mBytesPerPacket * 1024 / N_BUFFERS
(Where N_BUFFERS is the number of buffers and should be > 1 or playback is choppy.)
Here is an MCVE demonstrating the issue and solution:
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AudioToolbox/AudioQueue.h>
#import <AudioToolbox/AudioFile.h>
#define N_BUFFERS 2
#define N_BUFFER_PACKETS 1024
typedef struct AStreamData {
AudioFileID aFile;
AudioQueueRef aQueue;
AudioQueueBufferRef aBuffer[N_BUFFERS];
AudioStreamBasicDescription aDescription;
SInt64 pOffset;
volatile BOOL isRunning;
} AStreamData;
void printASBD(AudioStreamBasicDescription* desc) {
printf("mSampleRate = %d\n", (int)desc->mSampleRate);
printf("mBytesPerPacket = %d\n", desc->mBytesPerPacket);
printf("mFramesPerPacket = %d\n", desc->mFramesPerPacket);
printf("mBytesPerFrame = %d\n", desc->mBytesPerFrame);
printf("mChannelsPerFrame = %d\n", desc->mChannelsPerFrame);
printf("mBitsPerChannel = %d\n", desc->mBitsPerChannel);
}
void bufferCallback(
void *vData, AudioQueueRef aQueue, AudioQueueBufferRef aBuffer
) {
AStreamData* aData = (AStreamData*)vData;
UInt32 bRead = 0;
UInt32 pRead = (
aBuffer->mAudioDataBytesCapacity / aData->aDescription.mBytesPerPacket
);
OSStatus stat;
stat = AudioFileReadPackets(
aData->aFile, false, &bRead, NULL, aData->pOffset, &pRead, aBuffer->mAudioData
);
if(stat != 0) {
printf("AudioFileReadPackets returned %d\n", stat);
}
if(pRead == 0) {
aData->isRunning = NO;
return;
}
aBuffer->mAudioDataByteSize = bRead;
stat = AudioQueueEnqueueBuffer(aQueue, aBuffer, 0, NULL);
if(stat != 0) {
printf("AudioQueueEnqueueBuffer returned %d\n", stat);
}
aData->pOffset += pRead;
}
AStreamData* beginPlayback(NSURL* path) {
static AStreamData* aData;
aData = malloc(sizeof(AStreamData));
OSStatus stat;
stat = AudioFileOpenURL(
(CFURLRef)path, kAudioFileReadPermission, 0, &aData->aFile
);
printf("AudioFileOpenURL returned %d\n", stat);
UInt32 dSize = 0;
stat = AudioFileGetPropertyInfo(
aData->aFile, kAudioFilePropertyDataFormat, &dSize, 0
);
printf("AudioFileGetPropertyInfo returned %d\n", stat);
stat = AudioFileGetProperty(
aData->aFile, kAudioFilePropertyDataFormat, &dSize, &aData->aDescription
);
printf("AudioFileGetProperty returned %d\n", stat);
printASBD(&aData->aDescription);
stat = AudioQueueNewOutput(
&aData->aDescription, bufferCallback, aData, NULL, NULL, 0, &aData->aQueue
);
printf("AudioQueueNewOutput returned %d\n", stat);
aData->pOffset = 0;
for(int i = 0; i < N_BUFFERS; i++) {
// change YES to NO for stale playback
if(YES) {
stat = AudioQueueAllocateBuffer(
aData->aQueue,
aData->aDescription.mBytesPerPacket * N_BUFFER_PACKETS / N_BUFFERS,
&aData->aBuffer[i]
);
} else {
stat = AudioQueueAllocateBuffer(
aData->aQueue,
aData->aDescription.mBytesPerPacket,
&aData->aBuffer[i]
);
}
printf(
"AudioQueueAllocateBuffer returned %d for aBuffer[%d] with capacity %d\n",
stat, i, aData->aBuffer[i]->mAudioDataBytesCapacity
);
bufferCallback(aData, aData->aQueue, aData->aBuffer[i]);
}
UInt32 numFramesPrepared = 0;
stat = AudioQueuePrime(aData->aQueue, 0, &numFramesPrepared);
printf("AudioQueuePrime returned %d with %d frames prepared\n", stat, numFramesPrepared);
stat = AudioQueueStart(aData->aQueue, NULL);
printf("AudioQueueStart returned %d\n", stat);
UInt32 pSize = sizeof(UInt32);
UInt32 isRunning;
stat = AudioQueueGetProperty(
aData->aQueue, kAudioQueueProperty_IsRunning, &isRunning, &pSize
);
printf("AudioQueueGetProperty returned %d\n", stat);
aData->isRunning = !!isRunning;
return aData;
}
void endPlayback(AStreamData* aData) {
OSStatus stat = AudioQueueStop(aData->aQueue, NO);
printf("AudioQueueStop returned %d\n", stat);
}
NSString* getPath() {
// change NO to YES and enter path to hard code
if(NO) {
return #"";
}
char input[512];
printf("Enter file path: ");
scanf("%[^\n]", input);
return [[NSString alloc] initWithCString:input encoding:NSASCIIStringEncoding];
}
int main(int argc, const char* argv[]) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSURL* path = [NSURL fileURLWithPath:getPath()];
AStreamData* aData = beginPlayback(path);
if(aData->isRunning) {
do {
printf("Queue is running...\n");
[NSThread sleepForTimeInterval:1.0];
} while(aData->isRunning);
endPlayback(aData);
} else {
printf("Playback did not start\n");
}
[pool drain];
return 0;
}

iOS/Objective-C: library to connect to POP3

I'd like to connect to IMAP and POP3 servers, for IMAP I'm currently using MailCore. Unfortunately I don't find a suitable POP3-framwork.
I tried with libetpan:
mailpop3 * pop3;
int r;
pop3 = mailpop3_new(0, NULL);
r = mailpop3_ssl_connect(pop3, "pop.gmail.com", 995);
check_error(r, "connect failed");
but I always get a connection refused error; and it's only C, I would prefer Objective-C. Even better would be a library which I could use for both; IMAP and POP3.
I haven't used OCMail, but it seems like it's what you're looking for. It claims to support "POP3, IMAP4, SMTP, POPS, IMAPS, SMTPS".
Edit: Build Error
Turns out, the solution is actually in the README file.
Once you've downloaded the ZIP from Github, open the Xcode project.
Build for Profiling (Product Menu > Build For > Profiling (Command-Shift-I)).
Open Xcode preferences and go to "Locations"
Under Derived Data, next to the Advanced button you'll see a file path (something like /Users/YourUserName/Library/Developer/Xcode/DerivedData). There'll be a little arrow next to the path; click the arrow to go to that location in Finder.
It'll take you to a folder with all of your Xcode projects. Find the folder whose name starts with OCMail (and has a bunch of gibberish after it).
In that folder, find Build > Products > Debug-iphoneos > libOCMail.a. That's the library file you'll want to add into your Xcode project. Just drag it into your Xcode project and you should be good to go.
I got a bunch of errors building the project. They came from a badly defined enum type. Here's a cleaned up file:
http://cl.ly/code/442x2x3X3Y2I
Just download and replace the existing MimeMessage.m file before you build.
I was working with libetpan in past and I was connecting to pop3 server without problems, so I checked if it still working. I used code from here: https://github.com/dinhviethoa/libetpan/blob/master/tests/pop-sample.c and adjusted it for iOS.
If You use it, You will see a lot of warnings and app will crash after fetching first message, but connecting is working (of course, You need to enter Your email login and password).
I'm not saying that libetpan is good solution. When I was developing app with mail support I also used mailcore for IMAP and eventually resigned from POP3 support. But if You run from options it could be useful.
static void check_error(int r, char * msg)
{
if (r == MAILPOP3_NO_ERROR)
return;
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
-(IBAction)testButtonClick:(id)sender
{
mailpop3 * pop3;
int r;
carray * list;
unsigned int i;
// if (argc < 3) {
// fprintf(stderr, "syntax: pop-sample [gmail-email-address] [gmail- password]\n");
// exit(EXIT_FAILURE);
// }
mkdir("download", 0700);
pop3 = mailpop3_new(0, NULL);
r = mailpop3_ssl_connect(pop3, "pop.gmail.com", 995);
check_error(r, "connect failed");
r = mailpop3_user(pop3, #"mail login".cString);
check_error(r, "user failed");
r = mailpop3_pass(pop3, #"mail password".cString);
check_error(r, "pass failed");
r = mailpop3_list(pop3, &list);
check_error(r, "list failed");
NSLog(#"carray_count(list_: %d", carray_count(list));
for(i = 0 ; i < carray_count(list) ; i ++) {
struct mailpop3_msg_info * info;
char * msg_content;
size_t msg_size;
FILE * f;
char filename[512];
struct stat stat_info;
info = (mailpop3_msg_info *) carray_get(list, i);
if (info->msg_uidl == NULL) {
continue;
}
snprintf(filename, sizeof(filename), "download/%s.eml", info->msg_uidl);
r = stat(filename, &stat_info);
if (r == 0) {
printf("already fetched %u %s\n", info->msg_index, info->msg_uidl);
continue;
}
if(msg_content != NULL)
NSLog(#"msg_content: %#", [NSString stringWithUTF8String:msg_content]);
r = mailpop3_retr(pop3, info->msg_index, &msg_content, &msg_size);
check_error(r, "get failed");
// f = fopen(filename, "w");
// fwrite(msg_content, 1, msg_size, f);
// fclose(f);
// mailpop3_retr_free(msg_content);
if (info->msg_uidl != NULL) {
printf("fetched %u %s\n", info->msg_index, info->msg_uidl);
}
else {
printf("fetched %u\n", info->msg_index);
}
}
mailpop3_quit(pop3);
mailpop3_free(pop3);
// exit(EXIT_SUCCESS);
}

Very weird issue with AuthorizationExecuteWithPriveleges in Cocoa

I'm using AuthorizationExecuteWithPriveleges to execute bash commands from my App with admin privilege. I have found really weird issue. Here what I'm using
FILE *pipe=nil;
OSStatus err;
AuthorizationRef authorizationRef;
char *command= "/bin/chmod";
char *args[] = {"644","folderPath", nil};
if(err!=0)
{
err = AuthorizationCreate(nil,
kAuthorizationEmptyEnvironment,
kAuthorizationFlagDefaults,
&authorizationRef);
}
NSLog(#"test");
err = AuthorizationExecuteWithPrivileges(authorizationRef,
command,
kAuthorizationFlagDefaults,
args,
&pipe);
After calling this function about 40 times, it's starting respond very slowly. And after it is will just die,and freeze application, and I have no idea what is happening to this.It doesn't show the log "test", and doesn't do anything, after calling about 40 times.
It doesn't matter what Bash command or what arguments you are using. It still does the same thing. What is wrong with this ? The reason I'm using this, because my App needs to run on 10.5 as well.
Please if someone have idea, what can I do. I really appreciate it. I need ASAP. Thanks
Looked at this a bit more, and cooked up the following example, presented without warranty, but which works for me for thousands of invocations of AuthorizationExecuteWithPrivileges without issue:
void DoOtherStuff(AuthorizationRef auth, char* path);
void DoStuff(char* path)
{
AuthorizationItem foo;
foo.name = kAuthorizationRightExecute;
foo.value = NULL;
foo.valueLength = 0;
foo.flags = 0;
AuthorizationRights rights;
rights.count = 1;
rights.items = &foo;
AuthorizationRef authorizationRef;
OSStatus err = errAuthorizationSuccess;
if (errAuthorizationSuccess != (err = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef)))
{
NSLog(#"Error on AuthorizationCreate: %lu", (long)err);
return;
}
for (NSUInteger i = 0; i < 5000; i++)
{
NSLog(#"Doing run: %lu", (long)i+1);
DoOtherStuff(authorizationRef, "/tmp/foo");
}
if (errAuthorizationSuccess != (err = AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults)))
{
NSLog(#"Error on AuthorizationFree: %lu", (long)err);
return;
}
}
void DoOtherStuff(AuthorizationRef authorizationRef, char* path)
{
OSStatus err = errAuthorizationSuccess;
FILE *pipe = NULL;
#try
{
char *args[] = {"644", path, NULL};
if (errAuthorizationSuccess != (err = AuthorizationExecuteWithPrivileges(authorizationRef,
"/bin/chmod", kAuthorizationFlagDefaults, args, &pipe)))
{
NSLog(#"Error on AuthorizationExecuteWithPrivileges: %lu", (long)err);
return;
}
int stat;
wait(&stat);
NSLog(#"Success! Child Process Died!");
}
#finally
{
if (pipe)
fclose(pipe);
}
}
What Chris Suter said is dead on. What happens when you call AuthorizationExecuteWithPrivileges is that it fork()s your process and then exec()s the requested process (chmod in this case) from the child process. The child process won't be reaped until someone calls wait(), but that's hard because we don't get the PID of the child out of AuthorizationExecuteWithPrivileges (it would have been returned by fork()). As he said, if you're sure there aren't other threads spawning processes at the same time (i.e. your thread is the only one creating child processes), then you can just call the non-PID specific version of wait() like I do in this example.
If you don't call wait() then what happens is you accumulate these zombie child processes that are all waiting to be reaped. Eventually the OS says "no more."
I feel kinda bad posting this, since it's just a retread of what Chris Suter said; I've upvoted his answer.
For completeness, here's a reworked version of that example that achieves the goal by ignoring SIGCHLD instead of calling wait. It also is presented without warranty, but works for me.
void DoOtherStuff(AuthorizationRef auth, char* path);
void DoStuff(char* path)
{
AuthorizationItem foo;
foo.name = kAuthorizationRightExecute;
foo.value = NULL;
foo.valueLength = 0;
foo.flags = 0;
AuthorizationRights rights;
rights.count = 1;
rights.items = &foo;
AuthorizationRef authorizationRef;
OSStatus err = errAuthorizationSuccess;
struct sigaction oldAction;
struct sigaction newAction;
newAction.__sigaction_u.__sa_handler = SIG_IGN;
newAction.sa_mask = 0;
newAction.sa_flags = 0;
if(0 != sigaction(SIGCHLD, &newAction, &oldAction))
{
NSLog(#"Couldn't ignore SIGCHLD");
return;
}
#try
{
if (errAuthorizationSuccess != (err = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef)))
{
NSLog(#"Error on AuthorizationCreate: %lu", (long)err);
return;
}
for (NSUInteger i = 0; i < 1000; i++)
{
NSLog(#"Doing run: %lu", (long)i+1);
DoOtherStuff(authorizationRef, "/tmp/foo");
}
if (errAuthorizationSuccess != (err = AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults)))
{
NSLog(#"Error on AuthorizationFree: %lu", (long)err);
return;
}
}
#finally
{
const struct sigaction cOldAction = oldAction;
if(0 != sigaction(SIGCHLD, &cOldAction, NULL))
{
NSLog(#"Couldn't restore the handler for SIGCHLD");
return;
}
}
}
void DoOtherStuff(AuthorizationRef authorizationRef, char* path)
{
OSStatus err = errAuthorizationSuccess;
FILE *pipe = NULL;
#try
{
char *args[] = {"644", path, NULL};
if (errAuthorizationSuccess != (err = AuthorizationExecuteWithPrivileges(authorizationRef,
"/bin/chmod", kAuthorizationFlagDefaults, args, &pipe)))
{
NSLog(#"Error on AuthorizationExecuteWithPrivileges: %lu", (long)err);
return;
}
NSLog(#"Success!");
}
#finally
{
if (pipe)
fclose(pipe);
}
}
What you’re trying to do is not a good idea.
I would guess that you have a bug else where in your code, perhaps in the monitoring of the pipe. We need to see the rest of your code.
If you do pursue this approach, you will need to take care and make sure that you clean up zombie processes which can be awkward when using AuthorizationExecuteWithPrivileges because you don’t get the child process ID. You’ll either need to ignore SIGCHLD, or if you can be certain there are no other threads that are doing things with processes at the same time, you can just issue a call to wait.
You’ve also got to make sure you clean up the pipe as otherwise you’ll run out of file descriptors.
The system is far less forgiving about you leaking file descriptors or processes than it is about leaking memory.
The correct approach for your problem is probably to write a helper tool and then communicate with your helper tool asking it to perform privileged operations on your behalf. That way you’ll only be running your helper tool once. You should be able to read more about this in Apple’s documentation.
You should initialize err (due to the first IF statement), because it's no guaranteed to be 0. However, it probably is, so you are skipping AuthorizationCreate, so the authorized session isn't created.
Basically you are passing authorizationRef uninitialized to AuthorizationExecuteWithPrivileges which might be a problem.
Plus like others, I would put AuthorizationFree(authorizationRef,kAuthorizationFlagDefaults); at the end as well when you do use AuthorizationCreate to free the memory.
Also it's worth noting that AuthorizationExecuteWithPrivileges is deprecated as of OS X v10.7, but I think you know that since you said you are trying to run on 10.5
EDIT: You might want to check the status of err too after running AuthorizationCreate
if ( err != errAuthorizationSuccess ) {
return;
}
... you should check err after AuthorizationExecuteWithPrivileges as well.
I think I might know what's going on here: Try handling the pipe correctly (i.e. don't pass NULL, and make sure you close it). Weird stuff like this also happens with NSTask if you fail to give it a STDIN pipe. This page over at cocoadev.com explains:
An NSTask will break Xcode's debug log entirely if you execute
ANYTHING related with sh or bash (including scripts). printf, NSLog;
all will cease to function as soon as the task has been launched. Even
things like right clicking on an object in the debugger will yield
nothing (straight GDB still prints though). ... I figured out that the
problem lies with standard input, of all things. A quick fix for this
would be to set your standard input up to something random, like a
pipe, and do nothing with it.
This stumped me for hours and hours (albiet with NSTask and not AS). I would be surprised if the odd behavior you're seeing isn't related. Make sure you're not passing NULL, and then make sure that you're cleaning up the file handle that AuthorizationExecuteWithPrivileges creates for you, by calling fclose, etc.
I'm not 100% sure, but the "NSLog stopped working" symptom caught my eye.