diff options
author | Mikkel Krautz <mikkel@krautz.dk> | 2012-04-08 01:43:25 +0400 |
---|---|---|
committer | Mikkel Krautz <mikkel@krautz.dk> | 2012-04-08 01:43:25 +0400 |
commit | a975ad3fd53d4ec0506fd32d0d43673e38aa7d70 (patch) | |
tree | bbb7dfcce150e6e21120dcef39b9b72ab6b8df3e | |
parent | c9edba9ac283afd87ccee4a8da0deeee608589f5 (diff) |
MKCertificate, MKDistinguishedNameParser: implement and use DN parser.
-rw-r--r-- | MumbleKit.xcodeproj/project.pbxproj | 12 | ||||
-rw-r--r-- | src/MKCertificate.m | 25 | ||||
-rw-r--r-- | src/MKDistinguishedNameParser.h | 7 | ||||
-rw-r--r-- | src/MKDistinguishedNameParser.m | 238 |
4 files changed, 261 insertions, 21 deletions
diff --git a/MumbleKit.xcodeproj/project.pbxproj b/MumbleKit.xcodeproj/project.pbxproj index d7f2e26..d725889 100644 --- a/MumbleKit.xcodeproj/project.pbxproj +++ b/MumbleKit.xcodeproj/project.pbxproj @@ -9,6 +9,10 @@ /* Begin PBXBuildFile section */ 2808DD9413E4D8C0008448EA /* MKPacketDataStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 2845A7D1132D9C520034D631 /* MKPacketDataStream.h */; }; 2810DED514EA81D7004482F4 /* libCELT-0.7.0.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28492310132ED60700B4EAAC /* libCELT-0.7.0.a */; }; + 281EADA51530EA30000793AB /* MKDistinguishedNameParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 281EADA31530EA30000793AB /* MKDistinguishedNameParser.h */; }; + 281EADA61530EA30000793AB /* MKDistinguishedNameParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 281EADA31530EA30000793AB /* MKDistinguishedNameParser.h */; }; + 281EADA71530EA30000793AB /* MKDistinguishedNameParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 281EADA41530EA30000793AB /* MKDistinguishedNameParser.m */; }; + 281EADA81530EA30000793AB /* MKDistinguishedNameParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 281EADA41530EA30000793AB /* MKDistinguishedNameParser.m */; }; 282F6B0813624187008F555B /* MKServerPinger.h in Headers */ = {isa = PBXBuildFile; fileRef = 282F6B0613624187008F555B /* MKServerPinger.h */; }; 282F6B0913624187008F555B /* MKServerPinger.h in Headers */ = {isa = PBXBuildFile; fileRef = 282F6B0613624187008F555B /* MKServerPinger.h */; settings = {ATTRIBUTES = (Public, ); }; }; 282F6B0A13624187008F555B /* MKServerPinger.m in Sources */ = {isa = PBXBuildFile; fileRef = 282F6B0713624187008F555B /* MKServerPinger.m */; }; @@ -306,6 +310,8 @@ 2815C4E0150D559800136082 /* BetaDist.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BetaDist.xcconfig; path = cfg/BetaDist.xcconfig; sourceTree = "<group>"; }; 2815C4E1150D559800136082 /* Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = cfg/Release.xcconfig; sourceTree = "<group>"; }; 2815C4E6150D55BC00136082 /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = cfg/Debug.xcconfig; sourceTree = "<group>"; }; + 281EADA31530EA30000793AB /* MKDistinguishedNameParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MKDistinguishedNameParser.h; path = src/MKDistinguishedNameParser.h; sourceTree = SOURCE_ROOT; }; + 281EADA41530EA30000793AB /* MKDistinguishedNameParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MKDistinguishedNameParser.m; path = src/MKDistinguishedNameParser.m; sourceTree = SOURCE_ROOT; }; 282F6B0613624187008F555B /* MKServerPinger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MKServerPinger.h; path = src/MumbleKit/MKServerPinger.h; sourceTree = SOURCE_ROOT; }; 282F6B0713624187008F555B /* MKServerPinger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MKServerPinger.m; path = src/MKServerPinger.m; sourceTree = SOURCE_ROOT; }; 283363DC13EF535B00A04F04 /* MKChannelPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MKChannelPrivate.h; path = src/MKChannelPrivate.h; sourceTree = SOURCE_ROOT; }; @@ -482,6 +488,7 @@ 2845A792132D9C220034D631 /* MKVersion.m */, 2845A793132D9C220034D631 /* MulticastDelegate.m */, 2879526114C1BAB900567430 /* MKTextMessage.m */, + 281EADA41530EA30000793AB /* MKDistinguishedNameParser.m */, ); name = Sources; sourceTree = "<group>"; @@ -520,6 +527,7 @@ 2845A7D1132D9C520034D631 /* MKPacketDataStream.h */, 283363DC13EF535B00A04F04 /* MKChannelPrivate.h */, 283363DE13EF536C00A04F04 /* MKUserPrivate.h */, + 281EADA31530EA30000793AB /* MKDistinguishedNameParser.h */, ); name = "Private Headers"; sourceTree = "<group>"; @@ -658,6 +666,7 @@ 28F2FAD613E37BA000034BF2 /* MKAudioOutputUserPrivate.h in Headers */, 2808DD9413E4D8C0008448EA /* MKPacketDataStream.h in Headers */, 2879526914C1BDD800567430 /* MKTextMessage.h in Headers */, + 281EADA61530EA30000793AB /* MKDistinguishedNameParser.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -688,6 +697,7 @@ 28F2FAD513E37BA000034BF2 /* MKAudioOutputUserPrivate.h in Headers */, 283363DD13EF535B00A04F04 /* MKChannelPrivate.h in Headers */, 2879526814C1BDD800567430 /* MKTextMessage.h in Headers */, + 281EADA51530EA30000793AB /* MKDistinguishedNameParser.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -916,6 +926,7 @@ 28CC36FD132ED92500241269 /* ObjectivecDescriptor.pb.m in Sources */, 282F6B0B13624187008F555B /* MKServerPinger.m in Sources */, 2879526514C1BAB900567430 /* MKTextMessage.m in Sources */, + 281EADA81530EA30000793AB /* MKDistinguishedNameParser.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -944,6 +955,7 @@ 28CC36FC132ED92500241269 /* ObjectivecDescriptor.pb.m in Sources */, 282F6B0A13624187008F555B /* MKServerPinger.m in Sources */, 2879526414C1BAB900567430 /* MKTextMessage.m in Sources */, + 281EADA71530EA30000793AB /* MKDistinguishedNameParser.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/MKCertificate.m b/src/MKCertificate.m index d7c3d5c..4a018fd 100644 --- a/src/MKCertificate.m +++ b/src/MKCertificate.m @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #import <MumbleKit/MKCertificate.h> +#import "MKDistinguishedNameParser.h" #include <openssl/evp.h> #include <openssl/err.h> @@ -310,24 +311,6 @@ static int add_ext(X509 * crt, int nid, char *value) { return _derPrivKey != nil; } -// Parse a one-line UTF8 representation of subject or issuer info -// from a certificate. Returns a dictionary with the keys and values -// as-is. -- (NSDictionary *) copyDictForOneLineUTF8Repr:(NSData *)data { - NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; - NSString *str = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; - NSArray *components = [str componentsSeparatedByString:@", "]; - for (NSString *component in components) { - NSArray *pairs = [component componentsSeparatedByString:@" = "]; - if ([pairs count] != 2) { - [dict release]; - return nil; - } - [dict setObject:[pairs objectAtIndex:1] forKey:[pairs objectAtIndex:0]]; - } - return dict; -} - // Parse an ASN1 string representing time from an X509 PKIX certificate. - (NSDate *) copyAndParseASN1Date:(ASN1_TIME *)time { struct tm tm; @@ -395,7 +378,7 @@ static int add_ext(X509 * crt, int nid, char *value) { BUF_MEM *buf = NULL; BIO_get_mem_ptr(mem, &buf); NSData *data = [[NSData alloc] initWithBytes:buf->data length:buf->length]; - _subjectDict = [self copyDictForOneLineUTF8Repr:data]; + _subjectDict = [[MKDistinguishedNameParser parseName:data] retain]; [data release]; } BIO_free(mem); @@ -409,7 +392,7 @@ static int add_ext(X509 * crt, int nid, char *value) { BUF_MEM *buf = NULL; BIO_get_mem_ptr(mem, &buf); NSData *data = [[NSData alloc] initWithBytesNoCopy:buf->data length:buf->length freeWhenDone:NO]; - _issuerDict = [self copyDictForOneLineUTF8Repr:data]; + _issuerDict = [[MKDistinguishedNameParser parseName:data] retain]; [data release]; } BIO_free(mem); @@ -637,4 +620,4 @@ static int add_ext(X509 * crt, int nid, char *value) { return _privateKey; } -@end +@end
\ No newline at end of file diff --git a/src/MKDistinguishedNameParser.h b/src/MKDistinguishedNameParser.h new file mode 100644 index 0000000..ff9cbda --- /dev/null +++ b/src/MKDistinguishedNameParser.h @@ -0,0 +1,7 @@ +// Copyright 2012 The MumbleKit Developers. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +@interface MKDistinguishedNameParser : NSObject ++ (NSDictionary *) parseName:(NSData *)dn; +@end
\ No newline at end of file diff --git a/src/MKDistinguishedNameParser.m b/src/MKDistinguishedNameParser.m new file mode 100644 index 0000000..1a2a31e --- /dev/null +++ b/src/MKDistinguishedNameParser.m @@ -0,0 +1,238 @@ +// Copyright 2012 The MumbleKit Developers. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#import "MKDistinguishedNameParser.h" + +@interface MKDistinguishedNameParser () { + NSString *_name; + NSInteger _pos; + NSMutableArray *_pairs; + NSMutableArray *_put; +} +- (id) initWithName:(NSData *)dn; + +- (void) parse; +- (NSDictionary *) dictionaryRepresentation; + +- (unichar) getch; +- (void) putch:(unichar)c; +- (void) rejectWithReason:(NSString *)msg; + +- (void) scanAttribute; +- (void) scanAttributeName; +- (void) scanWhitespace; +- (void) scanAttributeValue; +- (void) scanCharater:(NSString *)str; +- (void) scanEquals; +- (void) scanComma; +- (void) scanAttributeValue; +- (NSString *) scanQuotedStringWithCharactersFromSet:(NSCharacterSet *)charSet; +@end + +@implementation MKDistinguishedNameParser + ++ (NSDictionary *) parseName:(NSData *)dn { + MKDistinguishedNameParser *parser = [[[MKDistinguishedNameParser alloc] initWithName:dn] autorelease]; + [parser parse]; + return [parser dictionaryRepresentation]; +} + +- (id) initWithName:(NSData *)dn { + if ((self = [super init])) { + // add a junk 0 char at the end of the string to ensure termination of the scanner + unichar nul = 0; + NSMutableString *nameString = [[NSMutableString alloc] initWithData:dn encoding:NSUTF8StringEncoding]; + [nameString appendString:[NSString stringWithCharacters:&nul length:1]]; + _name = nameString; + _pairs = [[NSMutableArray alloc] init]; + _put = [[NSMutableArray alloc] init]; + _pos = 0; + } + return self; +} + +- (void) dealloc { + [_pairs release]; + [_name release]; + [_put release]; + [super dealloc]; +} + +- (void) parse { + while (1) { + @try { + [self scanAttribute]; + } + @catch (NSException *exception) { + return; + } + } +} + +- (NSDictionary *) dictionaryRepresentation { + if (([_pairs count] % 2) != 0) { + return nil; + } + NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:[_pairs count]/2]; + for (int i = 0; i < [_pairs count]; i += 2) { + [dict setObject:[_pairs objectAtIndex:i+1] forKey:[_pairs objectAtIndex:i]]; + } + return dict; +} + +- (unichar) getch { + NSInteger putCount = [_put count]; + if (putCount > 0) { + unsigned long c = [[_put objectAtIndex:putCount-1] unsignedLongValue]; + [_put removeObjectAtIndex:putCount-1]; + return (unichar) c; + } + if (_pos > [_name length]-1) { + [self rejectWithReason:@"no more characters in string"]; + } + return [_name characterAtIndex:_pos++]; +} + +- (void) putch:(unichar)c { + [_put addObject:[NSNumber numberWithUnsignedLong:c]]; +} + +- (void) rejectWithReason:(NSString *)msg { + [NSException raise:@"MKDistinguishedNameParserException" format:@"%@", msg]; +} + +- (void) scanAttribute { + [self scanAttributeName]; + [self scanWhitespace]; + [self scanEquals]; + [self scanWhitespace]; + [self scanAttributeValue]; + [self scanComma]; + [self scanWhitespace]; +} + +- (void) scanAttributeName { + NSCharacterSet *letters = [NSCharacterSet letterCharacterSet]; + NSMutableString *attrName = [[[NSMutableString alloc] init] autorelease]; + + // Read at least a single 'letter'. + unichar c = [self getch]; + if (![letters characterIsMember:c]) + [self rejectWithReason:@"bad character in attribute name"]; + [attrName appendString:[NSString stringWithCharacters:&c length:1]]; + while (1) { + unichar c = [self getch]; + if (![letters characterIsMember:c]) { + [self putch:c]; + [_pairs addObject:attrName]; + return; + } else { + [attrName appendString:[NSString stringWithCharacters:&c length:1]]; + } + } +} + +- (void) scanWhitespace { + NSCharacterSet *whiteSpace = [NSCharacterSet whitespaceCharacterSet]; + + // Scan at least a single whitespace character. + unichar c = [self getch]; + if (![whiteSpace characterIsMember:c]) + [self rejectWithReason:@"expected at least 1 whitespace character"]; + while (1) { + unichar c = [self getch]; + if (![whiteSpace characterIsMember:c]) { + [self putch:c]; + return; + } + } +} + +- (void) scanCharater:(NSString *)str { + unichar matchChar = [str characterAtIndex:0]; + unichar c = [self getch]; + if (c != matchChar) { + [self rejectWithReason:[NSString stringWithFormat:@"expected `%C'", matchChar]]; + } +} + +- (void) scanEquals { + [self scanCharater:@"="]; +} + +- (void) scanComma { + [self scanCharater:@","]; +} + +- (void) scanAttributeValue { + NSMutableCharacterSet *allAllowedChars = [[NSMutableCharacterSet alloc] init]; + [allAllowedChars formUnionWithCharacterSet:[NSCharacterSet alphanumericCharacterSet]]; + [allAllowedChars formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]]; + [allAllowedChars formUnionWithCharacterSet:[NSCharacterSet symbolCharacterSet]]; + [allAllowedChars formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]]; + [allAllowedChars autorelease]; + + NSMutableCharacterSet *charsOutsideQuotes = [allAllowedChars mutableCopy]; + [charsOutsideQuotes removeCharactersInString:@","]; + [charsOutsideQuotes autorelease]; + + NSString *doubleQuote = @"\""; + unichar quoteChar = [doubleQuote characterAtIndex:0]; + + NSMutableString *attrValue = [[[NSMutableString alloc] init] autorelease]; + + unichar c = [self getch]; + if (c == quoteChar) { + [self putch:c]; + NSString *quotedString = [self scanQuotedStringWithCharactersFromSet:allAllowedChars]; + [attrValue appendString:quotedString]; + } else if (![charsOutsideQuotes characterIsMember:c]) { + [self rejectWithReason:@"unexpected character outside of quotes"]; + } else { + [attrValue appendString:[NSString stringWithCharacters:&c length:1]]; + } + + while (1) { + unichar c = [self getch]; + if (c == quoteChar) { + [self putch:c]; + NSString *quotedString = [self scanQuotedStringWithCharactersFromSet:allAllowedChars]; + [attrValue appendString:quotedString]; + } else if (![charsOutsideQuotes characterIsMember:c]) { + [_pairs addObject:attrValue]; + [self putch:c]; + return; + } else { + [attrValue appendString:[NSString stringWithCharacters:&c length:1]]; + } + } +} + +- (NSString *) scanQuotedStringWithCharactersFromSet:(NSCharacterSet *)charSet { + unichar c = [self getch]; + unichar quoteChar = [@"\"" characterAtIndex:0]; + unichar backSlashChar = [@"\\" characterAtIndex:0]; + + if (c != quoteChar) { + [self rejectWithReason:@"expected quoted string to start with `\"'"]; + } + + NSMutableString *quotedString = [[[NSMutableString alloc] init] autorelease]; + while (1) { + c = [self getch]; + if (c == quoteChar) { + return quotedString; + } else if (c == backSlashChar) { + c = [self getch]; + if (c != quoteChar) { + [self rejectWithReason:@"only quote-escapes are allowed inside quotes"]; + } + [quotedString appendString:@"\""]; + } else { + [quotedString appendString:[NSString stringWithCharacters:&c length:1]]; + } + } +} + +@end |