I wrote the below code to add a generic password from my app. SecKeychainAddGenericPassword() adds the app as a trusted application in the keychain by design.
I want to remove my app from the list of trusted apps. I called SecKeychainItemSetAccess () to do that but I still see my app listed as a trusted app.
addgenericpassword(const std::string& service,const std::string& account,
const std::string& password) {
SecKeychainItemRef item_ref;
OSStatus status = SecKeychainAddGenericPassword(NULL,
service.length(),
service.data(),
account.length(),
account.data(),
password.length(),
password.data(),
&item_ref);
//Creating an secAccess object that has an empty trusted application list
//https://developer.apple.com/documentation/security/1393522-secaccesscreate?language=objc
CFArrayRef applicationList=CFArrayCreate (NULL,NULL,0,NULL);
SecAccessRef accessref;
CFStringRef description=CFStringCreateWithCString(NULL, "Generic description", kCFStringEncodingASCII);
status = SecAccessCreate(description,applicationList,&accessref);
//Set the access of a keychain item "item_ref".
status = SecKeychainItemSetAccess(item_ref,accessref);
CFRelease(item_ref);
CFRelease(accessref);
CFRelease(applicationList);
CFRelease(description);
return 0;
}
Update:
Changed description to match the service name. Still no luck
CFStringRef description=CFStringCreateWithCString(NULL, service.data(), kCFStringEncodingASCII);
I have been able to get the functionality I was looking for.I am not sure if this is the right method to do it though.
SecAccessRef accessref;
SecKeychainItemCopyAccess(item_ref, &accessref);
CFArrayRef aclList;
SecAccessCopyACLList(accessref, &aclList);
CFIndex count = CFArrayGetCount(aclList);
//Array with 0 Applications / Empty Array . Not the same as passing NULL
CFArrayRef zero_applications = CFArrayCreate(NULL, NULL, 0, NULL);
for (int i = 0; i < count; i++) {
SecACLRef acl = (SecACLRef) CFArrayGetValueAtIndex(aclList, i);
CFArrayRef applicationList;
CFStringRef description;
CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector;
SecACLCopySimpleContents(acl, &applicationList, &description,
&promptSelector);
if (applicationList == NULL) {
continue;
}
CFIndex appCount = CFArrayGetCount(applicationList);
for (int j = 0; j < appCount; j++) {
status= SecACLSetContents(acl, zero_applications, description, 1);
break;
}
CFRelease(applicationList);
CFRelease(description);
}
// Set the modified copy to the item now
status = SecKeychainItemSetAccess(item_ref, accessref);
Related
I want to get a snapshot of the process info in the os x system.
The 'NSProcessInfo' can only get info of the calling process.
The ps cmd can be one solution, but i'd like a c or objective-c program.
Here's an example using using libproc.h to iterate over all the processes on the system and determine how many of them belong to the effective user of the process. You can easily modify this for your needs.
- (NSUInteger)maxSystemProcs
{
int32_t maxproc;
size_t len = sizeof(maxproc);
sysctlbyname("kern.maxproc", &maxproc, &len, NULL, 0);
return (NSUInteger)maxproc;
}
- (NSUInteger)runningUserProcs
{
NSUInteger maxSystemProcs = self.maxSystemProcs;
pid_t * const pids = calloc(maxSystemProcs, sizeof(pid_t));
NSAssert(pids, #"Memory allocation failure.");
const int pidcount = proc_listallpids(pids, (int)(maxSystemProcs * sizeof(pid_t)));
NSUInteger userPids = 0;
uid_t uid = geteuid();
for (int *pidp = pids; *pidp; pidp++) {
struct proc_bsdshortinfo bsdshortinfo;
int writtenSize;
writtenSize = proc_pidinfo(*pidp, PROC_PIDT_SHORTBSDINFO, 0, &bsdshortinfo, sizeof(bsdshortinfo));
if (writtenSize != (int)sizeof(bsdshortinfo)) {
continue;
}
if (bsdshortinfo.pbsi_uid == uid) {
userPids++;
}
}
free(pids);
return (NSUInteger)userPids;
}
I'm porting some old FSRef code to use NSURL, NSFileManager and friends. Everything works except setting and getting the Finder kIsStationery bit on the file.
Is there a way to do this without falling back on deprecated FSRef methods?
So after a bit of research here is the answer for setting and getting the stationery bit:
struct FileInfoBuf
{
u_int32_t info_length;
union
{
u_int32_t padding[8];
struct
{
u_int32_t type;
u_int32_t creator;
u_int16_t fdFlags;
u_int16_t location;
u_int32_t padding[4];
}
info;
}
data;
};
bool IsStationeryPad(const std::string& path)
{
attrlist attrList;
FileInfoBuf fileInfo;
attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
attrList.reserved = 0;
attrList.commonattr = ATTR_CMN_FNDRINFO;
attrList.volattr = 0;
attrList.dirattr = 0;
attrList.fileattr = 0;
attrList.forkattr = 0;
if (getattrlist(path.c_str(), &attrList, &fileInfo, sizeof(fileInfo), FSOPT_NOFOLLOW) == noErr)
{
return (CFSwapInt16BigToHost(fileInfo.data.info.fdFlags) & kIsStationery);
}
return false;
}
void SetStationeryPad(const std::string& path, bool isStationery)
{
OSErr err = noErr;
attrlist attrList;
FileInfoBuf fileInfo;
attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
attrList.reserved = 0;
attrList.commonattr = ATTR_CMN_FNDRINFO;
attrList.volattr = 0;
attrList.dirattr = 0;
attrList.fileattr = 0;
attrList.forkattr = 0;
err = getattrlist(path.c_str(), &attrList, &fileInfo, sizeof(fileInfo), FSOPT_NOFOLLOW);
if (err == noErr)
{
fileInfo.data.info.fdFlags |= CFSwapInt16HostToBig(kIsStationery);
setattrlist(path.c_str(), &attrList, &fileInfo.data, sizeof(fileInfo.data), FSOPT_NOFOLLOW);
}
}
Note that there is no real error checking in this code. Also, applications probably shouldn't be setting this bit, this is really a user decision and shuld be controlled through the Finder.
I am asking account's password(password for login in mac) in my application. How can I verify password which is entered user?
I think something like it, but it doesn't work:
-(BOOL)authenticatePassword:(char *)password adminName:(char *)userName
{
BOOL retValue = NO;
OSStatus status,status1;
AuthorizationFlags flag;
AuthorizationItem items[2];
items[0].name = kAuthorizationEnvironmentPassword;
items[0].value = password;
items[0].valueLength = strlen(password);
items[0].flags = 0;
items[1].name = kAuthorizationEnvironmentUsername;
items[1].value = userName;
items[1].valueLength = strlen(userName);
items[1].flags = 0;
AuthorizationItemSet itemSet = {2,items};
status = AuthorizationCreate(NULL, &itemSet, kAuthorizationFlagDefaults, &authorization_);
if(status == errAuthorizationSuccess) {
AuthorizationRights rights = {2,&items};
//AuthorizationEnvironment kEnviroment = {2, items};
AuthorizationFlags flag1 = kAuthorizationFlagDefaults;
status1 = AuthorizationCopyRights(authorization_, &rights,NULL, flag1, NULL);
if(status1 == errAuthorizationSuccess) {
retValue = YES;
}
}
return retValue;
}
In the AuthorizationCopyRightscall the user credentials for the validation should be in the environment parameter (your commented out line) and the rights parameter really should contain the rights you would like to gain using this user credentials.
The rights can contain built in rights or user created rights, it's simpler to use a built in one because creating a user defined right requires admin privilege.
This code bellow will do the trick for you, just call AuthenticateForRight with the username/password parameter and it will try to gain the allow right that is a built in one in the authorizationDB and requires a valid user credential.
To use with a custom right you should once call SetupAuthorizationForRight with admin rights for the right be created in the authenticationDB, after that you can check the user credentials anytime via AuthenticateForRight as a normal user just pass the rightName param also you passed for SetupAuthorizationForRight first time.
// original code: https://developer.apple.com/library/mac/#technotes/tn2095/_index.html
// https://developer.apple.com/library/mac/documentation/Security/Conceptual/authorization_concepts/03authtasks/authtasks.html#//apple_ref/doc/uid/TP30000995-CH206-BCIGEHDI
bool SetupAuthorizationForRight(const char* rightName)
// Called as the application starts up. Creates a connection
// to Authorization Services and then makes sure that our
// right is defined.
{
OSStatus err;
// Connect to Authorization Services.
AuthorizationRef authorization = NULL;
err = AuthorizationCreate(NULL, NULL, 0, &authorization);
// Set up our rights.
if (err == noErr) {
// Check whether our right is already defined.
err = AuthorizationRightGet(rightName, NULL);
if (err == noErr) {
// A right already exists, either set up in advance by
// the system administrator or because this is the second
// time we've run. Either way, there's nothing more for
// us to do.
} else if (err == errAuthorizationDenied) {
// The right is not already defined. Let's create a
// right definition based on the custom (not canned) rule defined
// in the dictionary below.
// The system administrator can modify this right as they
// see fit.
CFStringRef keys[2] = {CFSTR("class"), CFSTR("group")};
CFStringRef values[2] = {CFSTR("user"), CFSTR("everyone")};
// Allow access for every user - all of local and remote users are in the
// 'everyone' group, so this is a safe rule
CFDictionaryRef aDict = CFDictionaryCreate(NULL, (const void **)keys, (const void **)values, 2,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
err = AuthorizationRightSet(
authorization, // authRef
rightName, // rightName
aDict, // rightDefinition
CFSTR("Authenticate to log in via YourAppName."), // descriptionKey
NULL, // bundle, NULL indicates main
NULL // localeTableName,
); // NULL indicates "Localizable.strings"
if (aDict) {
CFRelease(aDict);
}
if (err != noErr) {
NSLog(#"Cannot set up authorization entry. Error: %d", err);
}
}
} else {
NSLog(#"Cannot open authorization database. Error: %d", err);
}
return (err == noErr);
}
bool AuthenticateForRight(const char* username, const char* password, const char* rightName)
{
OSStatus status = noErr;
if (rightName) {
if ((status = SetupAuthorizationForRight(rightName)) != noErr)
return false;
}
else
rightName = "allow"; // Allow right rule always defined by default and only authenticated users has this right
AuthorizationRef authRef = 0;
AuthorizationItem environment[2] = {{NULL, 0, NULL, 0}, {NULL, 0, NULL, 0}};
int numItems = 0;
if (username) {
AuthorizationItem item = { kAuthorizationEnvironmentUsername, strlen(username), (char*)username, 0 };
environment[numItems++] = item;
if (password) {
AuthorizationItem passItem = { kAuthorizationEnvironmentPassword, strlen(password), (char*)password, 0 };
environment[numItems++] = passItem;
}
}
AuthorizationItem right = {NULL, 0, NULL, 0};
right.name = rightName;
right.valueLength = 0;
right.value = 0;
AuthorizationRights rightSet = { 1, &right };
AuthorizationRights environmentSet = { static_cast<unsigned int>(numItems), environment };
status = AuthorizationCreate(NULL, &environmentSet, kAuthorizationFlagDefaults, &authRef);
if (status != noErr) {
NSLog(#"Cannot create authorization reference. Error: %d", status);
return false;
}
AuthorizationFlags flags = kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize; // | kAuthorizationFlagInteractionAllowed; <- Just for debugging, will display the OS auth dialog if needed!!!
status = AuthorizationCopyRights(authRef, &rightSet, &environmentSet, flags, NULL );
AuthorizationFree(authRef,kAuthorizationFlagDestroyRights);
return (status == noErr);
}
authorization services API will verify and prompt again if password is wrong.
Here is my code for your reference.
char *password = "password";
char *userName = "account";
AuthorizationRef authorization = NULL;
AuthorizationItem items[2];
items[0].name = kAuthorizationEnvironmentPassword;
items[0].value = password;
items[0].valueLength = strlen(password);
items[0].flags = 0;
items[1].name = kAuthorizationEnvironmentUsername;
items[1].value = userName;
items[1].valueLength = strlen(userName);
items[1].flags = 0;
AuthorizationRights rights = {2, items};
AuthorizationEnvironment enviroment = {2, items};
// Creates a new authorization reference and provides an option to authorize or preauthorize rights.
AuthorizationCreate(NULL, &enviroment, kAuthorizationFlagDefaults, &authorization);
AuthorizationFlags flag = kAuthorizationFlagDefaults| kAuthorizationFlagExtendRights;
OSStatus status = AuthorizationCopyRights(authorization, &rights, &enviroment, flag, NULL);
if(status == errAuthorizationSuccess)
{
NSLog(#"Pass");
}
else
{
NSLog(#"Fail");
}
I want to store SMTP-Data from my Mac OSX application using the keychain. I read the Keychain Services Programming Guide of Apple and wrote this method to store the data:
- (BOOL)saveSMPTData
{
OSStatus err;
SecKeychainItemRef item = nil;
SecProtocolType protocol = kSecProtocolTypeSMTP;
const char *accessLabelUTF8 = [KEYCHAIN_NAME UTF8String];
const char *serverNameUTF8 = [self.serverName UTF8String];
const char *usernameUTF8 = [self.username UTF8String];
const char *passwordUTF8 = [self.password UTF8String];
SecAccessRef access = createAccess(KEYCHAIN_NAME);
SecKeychainAttribute attrs[] = {
{ kSecLabelItemAttr, (int)strlen(accessLabelUTF8), (char *)accessLabelUTF8 },
{ kSecAccountItemAttr, (int)strlen(usernameUTF8), (char *)usernameUTF8 },
{ kSecServerItemAttr, (int)strlen(serverNameUTF8), (char *)serverNameUTF8 },
{ kSecProtocolItemAttr, sizeof(SecProtocolType), (SecProtocolType *)&protocol }
};
SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
err = SecKeychainItemCreateFromContent(kSecInternetPasswordItemClass,
&attributes,
(int)strlen(passwordUTF8),
passwordUTF8,
NULL,
access,
&item);
if (access) CFRelease(access);
if (item) CFRelease(item);
return (err == noErr);
}
SecAccessRef createAccess(NSString *accessLabel)
{
OSStatus err;
SecAccessRef access = nil;
NSArray *trustedApplications = nil;
SecTrustedApplicationRef myself;
err = SecTrustedApplicationCreateFromPath(NULL, &myself);
trustedApplications = [NSArray arrayWithObjects:(__bridge id)myself, nil];
err = SecAccessCreate((__bridge CFStringRef)accessLabel,
(__bridge CFArrayRef)trustedApplications, &access);
if (err) return nil;
return access;
}
Of course I also want to load them. My first try looks like this:
- (BOOL)loadDataFromKeychain
{
uint32_t serverNameLength = 0;
const char *serverName = NULL;
uint32_t usernameLength = 0;
const char *username = NULL;
uint32_t passwordLength = 0;
void **password = NULL;
OSStatus err = SecKeychainFindInternetPassword(NULL,
serverNameLength, serverName,
0, NULL,
usernameLength, username,
0, NULL,
0, 0,
0,
&passwordLength, password,
NULL); // How do I get the ItemRef?
return (err == noErr);
}
But this does not work, and I think I know why not. I don’t know how to get the SecKeychainItemRef for the SecKeychainFindInternetPassword method.
Maybe anyone can help me?
Instead of declaring password a void **, declare it a void * and pass &password for the second-to-last parameter.
You probably don't need the SecKeychainItemRef for what you're trying to accomplish.
By the way, have you tried using Keychain Access to verify the items are getting into the keychain?
Client (iOS) sends a message and the server checks it and answers.
iOS displays the answer.
There are two types of answer. One is just an answer. Server sends only one time.
The other is a little different. Server sends 20 times.
When server sends one time, I can process well. It's not difficult.
The problem is with the second type:
I tried two way of getting the data.
First, I used a simple CFSocket example with some modification. When it gets a message, it works well. When it gets 20 messages, it stops with an error. It says "Program received signal 'SIGABRT' "
//main code
CFSocketRef ref = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_DGRAM, 0, kCFSocketReadCallBack|kCFSocketDataCallBack|kCFSocketConnectCallBack|kCFSocketWriteCallBack, CFSockCallBack, NULL);
struct sockaddr_in theName;
struct hostent *hp;
theName.sin_port = htons(5003);
theName.sin_family = AF_INET;
hp = gethostbyname(IPADDRESS);
if( hp == NULL ) {
return;
}
memcpy( &theName.sin_addr.s_addr, hp->h_addr_list[PORT_NUM], hp->h_length );
CFDataRef addressData = CFDataCreate( NULL, &theName, sizeof( struct sockaddr_in ) );
CFSocketConnectToAddress(ref, addressData, 30);
CFRunLoopSourceRef FrameRunLoopSource = CFSocketCreateRunLoopSource(NULL, ref , 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), FrameRunLoopSource, kCFRunLoopCommonModes);
//Callback Method
void CFSockCallBack (
CFSocketRef s,
CFSocketCallBackType callbackType,
CFDataRef address,
const void *data,
void *info
) {
NSLog(#"callback!");
if(callbackType == kCFSocketDataCallBack) {
[lock lock];
NSLog(#"has data");
UInt8 * d = CFDataGetBytePtr((CFDataRef)data);
int len = CFDataGetLength((CFDataRef)data);
for(int i=0; i < len; i++) {
// NSLog(#"%c",*(d+i));
}
//Data processing area=
[lock unlock];
}
if(callbackType == kCFSocketReadCallBack) {
NSLog(#"to read");
char buf[100] = {0};
int sock = CFSocketGetNative(s);
NSLog(#"to read");
NSLog(#"read:%d",recv(sock, &buf, 100, 0));
NSLog(#"%s",buf);
}
if(callbackType == kCFSocketWriteCallBack) {
NSLog(#"to write");
char sendbuf[100]={0x53, 0x4D, 0x49, 0x43, 0x2};
//strcpy(sendbuf,"GET / HTTP/1.0\r\n\r\n");
//NSLog(#"%c",0x53);
CFDataRef dt = CFDataCreate(NULL, sendbuf, 5);
CFSocketSendData(s, NULL, dt, strlen(sendbuf));
}
if(callbackType == kCFSocketConnectCallBack) {
NSLog(#"connected");
}
}
The second way is using CocoaAsyncSocket. It seems to work well. But sometimes it stops with an error.
I used GCDAsyncUDPSocket. It stops randomly with signal "EXC_BAD_ACCESS".
I did it well on Android... So I think there is some other way possible.