Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
PPCToolboxKeychain.c
/* |
File: PPCToolboxKeychain.c |
Contains: 'osax' for creating PPC Toolbox keys in the keychain. |
Written by: Quinn "The Eskimo!" |
Copyright: Copyright © 2000 by Apple Computer, Inc., All Rights Reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Change History (most recent first): |
*/ |
///////////////////////////////////////////////////////////////// |
// MIB Setup |
#include "MoreSetup.h" |
// Mac OS Interfaces |
#include <Keychain.h> |
#include <CodeFragments.h> |
#include <StringCompare.h> |
#include <OpenTransportProviders.h> |
// Standard C interfaces |
#include <ctype.h> |
#include <stdlib.h> |
// MIB interfaces |
#include "MoreAppleEvents.h" |
// Apple Event Constants |
#include "PPCToolboxKeychain.h" |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Global Data ----- |
static FSSpec gFragmentFSSpec; // location of osax file |
static AEEventHandlerUPP gBlessKeyHandlerUPP = nil; // -> BlessKeyHandlerProc |
static Boolean gHaveInstalled = false; // have we installed our event handler |
__declspec(export) UInt32 gAdditionReferenceCount = 0; // required by AppleScript to prevent unload while busy |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Event Handlers ----- |
static Boolean CheckForChars(const char **cursor, const char *templ) |
// Assuming the *cursor is a pointer to an input string and |
// templ is a pointer to a constant template string, |
// check that *cursor starts with the characters in the template, |
// return true if the template is found or false otherwise. |
// Also updates *cursor as it ÔconsumesÕ characters. |
// |
// For example, if *cursor points to "eppc://foo" and |
// templ points to "eppc://", this will return true |
// and update *cursor to point to the the "f" in "foo". |
{ |
MoreAssertQ(cursor != nil); |
MoreAssertQ(*cursor != nil); |
MoreAssertQ(templ != nil); |
while (**cursor == *templ && *templ != 0) { |
*cursor += 1; |
templ += 1; |
} |
return *templ == 0; |
} |
static Boolean CollectChars(const char **cursor, char terminator, UInt8 *buffer, ByteCount bufferSize, ByteCount *actualSize) |
// Copies characters from an input string (pointed to be *cursor ) to an output buffer |
// (described by buffer and bufferSize), terminating when either a) we hit a null terminator |
// in *cursor, or when we hit the terminator character in the input string. Updates |
// *cursor to point to the terminating character. Also returns, in *actualSize, the number |
// of bytes that were copied to the buffer. Converts escape sequences (%xx) into the |
// appropriate character in the output buffer. |
// |
// Returns true if the copy was successful, or false if it failed. Reasons for failure |
// include a) running out of space in the output buffer, b) a bogus escape sequence, or |
// c) not copying any characters to the output buffer (null output is not allowed). |
{ |
Boolean result; |
MoreAssertQ(cursor != nil); |
MoreAssertQ(*cursor != nil); |
MoreAssertQ(buffer != nil); |
MoreAssertQ(actualSize != nil); |
*actualSize = 0; |
result = true; |
do { |
// If weÕre run out of input characters, leave with result true. |
if ( **cursor == 0 || **cursor == terminator ) { |
break; |
} |
// We know weÕre going for another output character. If we donÕt have enough |
// space, fail with an error. |
if ( *actualSize == bufferSize ) { |
result = false; |
break; |
} |
// Now look for a '%'; if we find one, interpret the digits that follow as hex. |
// Otherwise just copy the character across. |
if (**cursor == '%') { |
char tmpStr[3]; |
*cursor += 1; // skip '%' |
tmpStr[0] = (*cursor)[0]; |
tmpStr[1] = (*cursor)[1]; |
tmpStr[2] = 0; |
if ( ! isxdigit(tmpStr[0]) || ! isxdigit(tmpStr[1]) ) { |
result = false; |
break; |
} |
*cursor += 2; // skip hex digits |
buffer[*actualSize] = (UInt8) strtoul(tmpStr, nil, 16);; |
} else { |
buffer[*actualSize] = (UInt8) **cursor; |
*cursor += 1; |
} |
*actualSize += 1; |
} while (true); |
// If we didnÕt copy across /any/ characters, thatÕs an error. |
if (result && *actualSize == 0) { |
result = false; |
} |
return result; |
} |
static OSStatus ParseURLIntoGenericData(const char *url, UInt8 *genaBuffer, ByteCount *genaBufferSize) |
// Parses strings of the form eppc://dns.name or eppc:/at/machine:zone |
// into the appropriate 'gena' attribute format (either a packed NBPEntity |
// or a LocationNameRec with locationKindSelector of ppcXTIAddrLocation). |
{ |
OSStatus err; |
const char *cursor; |
LocationNamePtr loc; |
ByteCount actualSize; |
UInt8 *outputCursor; |
MoreAssertQ(url != nil); |
MoreAssertQ(genaBuffer != nil); |
MoreAssertQ(genaBufferSize != nil); |
err = -1; |
cursor = url; |
if ( ! CheckForChars(&cursor, "eppc:/") ) goto parseError; |
switch (*cursor) { |
case '/': |
// eppc://dns.name |
// For TCP/IP, the 'gena' attribute for a PPC Toolbox key is in the |
// same LocationNameRec format used when sending Apple events over TCP/IP. |
// See DTS Technote 1176 for more details. |
// |
// <http://developer.apple.com/technotes/tn/tn1176.html#ppctoolbox> |
cursor += 1; |
loc = (LocationNamePtr) genaBuffer; |
loc->locationKindSelector = ppcXTIAddrLocation; |
loc->u.xtiType.Reserved[0] = 0; |
loc->u.xtiType.Reserved[1] = 0; |
loc->u.xtiType.Reserved[2] = 0; |
loc->u.xtiType.xtiAddr.fAddressType = AF_DNS; |
if ( ! CollectChars(&cursor, 0, loc->u.xtiType.xtiAddr.fAddress, sizeof(loc->u.xtiType.xtiAddr.fAddress), &actualSize) ) goto parseError; |
if ( *cursor != 0) goto parseError; |
loc->u.xtiType.xtiAddrLen = sizeof(PPCXTIAddressType) + actualSize; |
*genaBufferSize = loc->u.xtiType.xtiAddrLen + sizeof(loc->locationKindSelector) |
+ sizeof(loc->u.xtiType.Reserved) |
+ sizeof(loc->u.xtiType.xtiAddrLen); |
break; |
case 'a': |
// eppc:/at/machine:zone |
// For AppleTalk, the 'gena' attribute for a PPC Toolbox key is in the |
// standard NBPEntity format (ie three packed Pascal strings, each of |
// 32 characters or less). |
if ( ! CheckForChars(&cursor, "at/") ) goto parseError; |
outputCursor = genaBuffer; |
if ( ! CollectChars(&cursor, ':', outputCursor + 1, 32, &actualSize) ) goto parseError; |
if ( *cursor != ':') goto parseError; |
cursor += 1; |
*outputCursor = actualSize; |
outputCursor += *outputCursor + 1; |
BlockMove("\pPPCToolBox", outputCursor, 11); |
outputCursor += 11; |
if ( ! CollectChars(&cursor, '0', outputCursor + 1, 32, &actualSize) ) goto parseError; |
if ( *cursor != 0) goto parseError; |
*outputCursor = actualSize; |
outputCursor += *outputCursor + 1; |
*genaBufferSize = outputCursor - genaBuffer; |
break; |
default: |
goto parseError; |
} |
err = noErr; |
parseError: |
return err; |
} |
static pascal OSErr BlessKeyHandlerProc(const AppleEvent *theAppleEvent, AppleEvent *reply, UInt32 handlerRefcon) |
// The Apple event handler installed by our osax's fragment initialisation routine. |
// This handler extracts the event parameters ("name" and "addr"), looks up the |
// generic key by name in the default keychain, then parses the "addr" URL into |
// a 'gena' key attribute and stores that attribute in the key. |
{ |
OSStatus err; |
OSStatus junk; |
AEDesc nameDesc; |
AEDesc addrDesc; |
Str255 name; |
char addr[256]; |
KCItemRef foundItem; |
UInt8 genaBuffer[256]; |
ByteCount genaBufferSize; |
MoreAssertQ(theAppleEvent != nil); |
MoreAssertQ(reply != nil); |
#if MORE_DEBUG |
MoreAssertQ(handlerRefcon == 0); |
#else |
#pragma unused(handlerRefcon) |
#endif |
gAdditionReferenceCount += 1; // tell AppleScript weÕre busy |
MoreAENullDesc(&nameDesc); |
MoreAENullDesc(&addrDesc); |
foundItem = nil; |
err = noErr; |
if ( !(KeychainManagerAvailable()) ) { |
err = unimpErr; |
} |
err = AEGetParamDesc(theAppleEvent, keyAEName, typeText, &nameDesc); |
if (err == noErr) { |
err = MoreAEGetPStringFromDescriptor(&nameDesc, name); |
} |
if (err == noErr) { |
err = AEGetParamDesc(theAppleEvent, kAEISClientAddress, typeText, &addrDesc); |
} |
if (err == noErr) { |
err = MoreAEGetCStringFromDescriptor(&addrDesc, addr); |
} |
if (err == noErr) { |
err = MoreAEGotRequiredParams(theAppleEvent); |
} |
if (err == noErr) { |
err = KCFindGenericPassword(name, nil, 0, nil, 0, &foundItem); |
} |
if (err == noErr) { |
genaBufferSize = sizeof(genaBuffer); |
err = ParseURLIntoGenericData(addr, genaBuffer, &genaBufferSize); |
} |
if (err == noErr) { |
KCAttribute attr; |
attr.tag = kGenericKCItemAttr; |
attr.length = genaBufferSize; |
attr.data = genaBuffer; |
err = KCSetAttribute(foundItem, &attr); |
} |
if (err == noErr) { |
err = KCUpdateItem(foundItem); |
} |
if (reply->dataHandle != nil) { |
// Need to put the an error string into the keyErrorString parameter of |
// reply here. Only necessary for errors that AppleScript doesnÕt already |
// know about, such as the unimpErr we generate if Keychain isn't available. |
// This code is left as an exercise for the reader. |
} |
if (foundItem != nil) { |
junk = KCReleaseItem(&foundItem); |
MoreAssertQ(junk == noErr); |
} |
MoreAEDisposeDesc(&nameDesc); |
MoreAEDisposeDesc(&addrDesc); |
gAdditionReferenceCount -= 1; // tell AppleScript weÕre no longer busy |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Init/Term ----- |
extern OSErr FragmentInit(const CFragInitBlock *initBlock); |
extern void FragmentTerm(void); |
extern OSErr FragmentInit(const CFragInitBlock *initBlock) |
// This routine is the native osax's fragment initialization |
// routine. ItÕs responsible for installing our event handler. |
{ |
OSStatus err; |
#if MORE_DEBUG |
DebugStr("\pFragmentInit"); |
#endif |
MoreAssertQ(gAdditionReferenceCount == 0); // Either the data initialization failed, or something else bad has happened. |
// We latch the FSSpec of the 'osax' file in case we need to access |
// our resource fork. We currently donÕt use this info, but a future |
// extensions might use it (to get better error numbers, for example). |
MoreAssertQ(initBlock->fragLocator.where == kDataForkCFragLocator); |
gFragmentFSSpec = *(initBlock->fragLocator.u.onDisk.fileSpec); |
// Install the handler. |
err = noErr; |
gBlessKeyHandlerUPP = NewAEEventHandlerUPP(BlessKeyHandlerProc); |
if (gBlessKeyHandlerUPP == nil) { |
err = memFullErr; |
} |
if (err == noErr) { |
err = AEInstallEventHandler(kBlessKeyClassID, kBlessKeyEventID, gBlessKeyHandlerUPP, 0, true); |
gHaveInstalled = (err == noErr); |
} |
// Clean up. |
if (err != noErr) { |
FragmentTerm(); |
} |
return err; |
} |
extern void FragmentTerm(void) |
// This routine is the native osax's fragment termination |
// routine. ItÕs responsible for tearing down any initialization |
// done by FragmentInit. |
{ |
OSStatus junk; |
#if MORE_DEBUG |
DebugStr("\pFragmentTerm"); |
#endif |
MoreAssertQ(gAdditionReferenceCount == 0); // Why are we being unloaded if weÕre busy? |
if (gHaveInstalled) { |
MoreAssertQ(gBlessKeyHandlerUPP != nil); |
junk = AERemoveEventHandler(kBlessKeyClassID, kBlessKeyEventID, gBlessKeyHandlerUPP, true); |
MoreAssertQ(junk == noErr); |
gHaveInstalled = false; |
} |
if (gBlessKeyHandlerUPP != nil) { |
DisposeAEEventHandlerUPP(gBlessKeyHandlerUPP); |
gBlessKeyHandlerUPP = nil; |
} |
} |
#ifdef PPCTOOLBOX_KEYCHAIN_TESTBED |
// The code inside this conditional is only used by the Testbed |
// target to create a testbed application that letÕs us debug |
// the core of the 'osax' without having to mess around debugging |
// code resources. |
#include <stdio.h> |
static void TestParseURL(const char *url) |
{ |
OSStatus err; |
UInt8 genaBuf[256]; |
ByteCount genaBufSize; |
ByteCount thisByteIndex; |
genaBufSize = sizeof(genaBuf); |
err = ParseURLIntoGenericData(url, genaBuf, &genaBufSize); |
if (err == noErr) { |
printf("ParseURLIntoGenericData(%s) = \n", url); |
for (thisByteIndex = 0; thisByteIndex < genaBufSize; thisByteIndex++) { |
printf("%02x ", genaBuf[thisByteIndex]); |
} |
printf("\n"); |
} else { |
printf("¥¥¥ ParseURLIntoGenericData(%s), err = %ld\n", url, err); |
} |
} |
extern void main(void) |
{ |
printf("PPCToolboxKeychain Testbed!\n"); |
// Ã means test should succeed |
// x means test should fail |
TestParseURL("eppc://guy-smiley.apple.com"); // Ã |
TestParseURL("eppc:/at/Guy Smiley:*"); // Ã |
TestParseURL("foo"); // x |
TestParseURL("eppc:"); // x |
TestParseURL("eppc:/"); // x |
TestParseURL("eppc://"); // x |
TestParseURL("eppc:/bob/"); // x |
TestParseURL("eppc:/ab/"); // x |
TestParseURL("eppc:/at/"); // x |
TestParseURL("eppc:/at/machine"); // x |
TestParseURL("eppc:/at/machine:"); // x |
TestParseURL("eppc:/at/:"); // x |
TestParseURL("eppc:/at/:zone"); // x |
TestParseURL("eppc:/at/machine:zone"); // Ã |
TestParseURL("eppc:/at/machine%3Awith%3Acolons:zone"); // Ã |
TestParseURL("eppc:/at/01234567890123456789012345678901:zone"); // Ã |
TestParseURL("eppc:/at/012345678901234567890123456789012:zone"); // x |
TestParseURL("eppc://%"); // x |
TestParseURL("eppc://%x"); // x |
TestParseURL("eppc://%xy"); // x |
printf("Done. Press command-Q to Quit.\n"); |
} |
#endif |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-03-14