mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
Changes to avoid placing sensitive information in debug logs.
This commit is contained in:
parent
cc38f2f4a1
commit
1934ce6205
4 changed files with 212 additions and 18 deletions
11
ChangeLog
11
ChangeLog
|
@ -1,3 +1,14 @@
|
|||
2022-05-20 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
Changes to remove sensitive information from debug logs.
|
||||
* Source/Additions/GSMime.m: Mask out sensitive information in the
|
||||
-description method of the Authorization header. Add method to do
|
||||
similar masking for debug output of HTTP requests.
|
||||
* Source/GSHTTPURLHandle.m: Use new method to mask Authorization
|
||||
header value in debug log of outgoing requests.
|
||||
* Source/NSURLProtocol.m: Use new method to mask Authorization
|
||||
header value in debug log of outgoing requests.
|
||||
|
||||
2022-04-14 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Source/NSConnection.m: If we get an exception in a packet from a
|
||||
|
|
|
@ -3512,6 +3512,97 @@ static NSCharacterSet *tokenSet = nil;
|
|||
return [self makeToken: t preservingCase: NO];
|
||||
}
|
||||
|
||||
/* Used to put headers in the HTTP requests we generate.
|
||||
* If masked is non-NULL then the data object it points to is modified to
|
||||
* make it a version of the request with authenticiation hidden (for debug
|
||||
* logging). If the receiver is an authentication header and masked is not
|
||||
* NULL but points to a nil object, an autoreleased mutable data object is
|
||||
* created to hold the debug information.
|
||||
*/
|
||||
- (void) addToBuffer: (NSMutableData*)buf
|
||||
masking: (NSMutableData**)masked
|
||||
{
|
||||
NSUInteger pos = [buf length];
|
||||
BOOL maskThis = NO;
|
||||
|
||||
if (masked)
|
||||
{
|
||||
NSString *n = [self name];
|
||||
|
||||
if ([n isEqualToString: @"authorization"])
|
||||
{
|
||||
NSUInteger len = [*masked length];
|
||||
|
||||
maskThis = YES;
|
||||
if (0 == len)
|
||||
{
|
||||
*masked = AUTORELEASE([buf mutableCopy]);
|
||||
}
|
||||
else if (len < pos)
|
||||
{
|
||||
[*masked appendBytes: [buf bytes] + len
|
||||
length: pos - len];
|
||||
}
|
||||
}
|
||||
}
|
||||
[buf appendData: [self rawMimeDataPreservingCase: YES foldedAt: 0]];
|
||||
if (masked && *masked)
|
||||
{
|
||||
NSUInteger len = [buf length];
|
||||
const uint8_t *from = [buf bytes];
|
||||
|
||||
if (maskThis)
|
||||
{
|
||||
uint8_t *to;
|
||||
uint8_t c;
|
||||
|
||||
[*masked setLength: len];
|
||||
to = [*masked mutableBytes];
|
||||
memcpy(to + pos, from + pos, 14); // Authorization:
|
||||
pos += 14;
|
||||
|
||||
/* Show spaces before scheme
|
||||
*/
|
||||
while (pos < len && isspace((c = from[pos])))
|
||||
{
|
||||
to[pos++] = c;
|
||||
}
|
||||
|
||||
/* Show authorisation scheme
|
||||
*/
|
||||
while (pos < len && ((c = from[pos]) == '-' || isalnum(c)))
|
||||
{
|
||||
to[pos++] = c;
|
||||
}
|
||||
|
||||
/* Show spaces after scheme
|
||||
*/
|
||||
while (pos < len && isspace((c = from[pos])))
|
||||
{
|
||||
to[pos++] = c;
|
||||
}
|
||||
|
||||
/* Mask out everything apart from line wrapping/termination.
|
||||
*/
|
||||
while (pos < len)
|
||||
{
|
||||
uint8_t c = from[pos];
|
||||
|
||||
if (c != '\n' && c != '\r' && c != '\t')
|
||||
{
|
||||
c = '*';
|
||||
}
|
||||
to[pos++] = c;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[*masked appendBytes: from + pos
|
||||
length: len - pos];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (id) copyWithZone: (NSZone*)z
|
||||
{
|
||||
GSMimeHeader *c;
|
||||
|
@ -3542,18 +3633,68 @@ static NSCharacterSet *tokenSet = nil;
|
|||
|
||||
- (NSString*) description
|
||||
{
|
||||
NSString *desc;
|
||||
NSString *desc = [super description];
|
||||
NSString *n = [self name];
|
||||
NSString *v = [self value];
|
||||
NSDictionary *p = [self parameters];
|
||||
|
||||
if ([p count] > 0)
|
||||
if ([n isEqualToString: @"authorization"])
|
||||
{
|
||||
desc = [NSString stringWithFormat: @"%@ %@: %@ params: %@",
|
||||
[super description], [self name], [self value], p];
|
||||
NSRange r = [v rangeOfCharacterFromSet: whitespace];
|
||||
NSString *scheme;
|
||||
|
||||
if (r.length > 0)
|
||||
{
|
||||
scheme = [v substringToIndex: r.location];
|
||||
v = [v substringFromIndex: NSMaxRange(r)];
|
||||
}
|
||||
else
|
||||
{
|
||||
scheme = v;
|
||||
v = @"";
|
||||
}
|
||||
if ([p count] > 0)
|
||||
{
|
||||
if ([scheme caseInsensitiveCompare: @"digest"] == NSOrderedSame)
|
||||
{
|
||||
NSMutableDictionary *m = AUTORELEASE([p mutableCopy]);
|
||||
|
||||
/* For a digest, it's good for debug to be able to see the
|
||||
* digest parameters. Only the 'response' is sensitive.
|
||||
*/
|
||||
[m setObject: @"masked" forKey: @"response"];
|
||||
v = [v stringByTrimmingSpaces];
|
||||
if ([v length] > 0)
|
||||
{
|
||||
v = @"value-masked";
|
||||
}
|
||||
desc = [NSString stringWithFormat: @"%@ %@: %@ params: %@",
|
||||
desc, n, v, m];
|
||||
}
|
||||
else
|
||||
{
|
||||
desc = [NSString stringWithFormat: @"%@ %@: value-masked",
|
||||
desc, n];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
desc = [NSString stringWithFormat: @"%@ %@: value-masked",
|
||||
desc, n];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
desc = [NSString stringWithFormat: @"%@ %@: %@",
|
||||
[super description], [self name], [self value]];
|
||||
if ([p count] > 0)
|
||||
{
|
||||
desc = [NSString stringWithFormat: @"%@ %@: %@ params: %@",
|
||||
desc, n, v, p];
|
||||
}
|
||||
else
|
||||
{
|
||||
desc = [NSString stringWithFormat: @"%@ %@: %@",
|
||||
desc, n, v];
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
@ -4566,7 +4707,7 @@ appendString(NSMutableData *m, NSUInteger offset, NSUInteger fold,
|
|||
|
||||
/**
|
||||
* Returns the value of this header (excluding any parameters).<br />
|
||||
* Use the -fullValue m,ethod if you want parameter included.
|
||||
* Use the -fullValue method if you want parameter included.
|
||||
*/
|
||||
- (NSString*) value
|
||||
{
|
||||
|
|
|
@ -65,6 +65,11 @@
|
|||
# include <sys/socket.h> // For MSG_PEEK, etc
|
||||
#endif
|
||||
|
||||
@interface GSMimeHeader (HTTPRequest)
|
||||
- (void) addToBuffer: (NSMutableData*)buf
|
||||
masking: (NSMutableData**)masked;
|
||||
@end
|
||||
|
||||
/*
|
||||
* Implement map keys for strings with case insensitive comparisons,
|
||||
* so we can have case insensitive matching of http headers (correct
|
||||
|
@ -427,6 +432,7 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
|
|||
NSString *key;
|
||||
NSString *val;
|
||||
NSMutableData *buf;
|
||||
NSMutableData *masked = nil;
|
||||
NSString *version;
|
||||
NSMapEnumerator enumerator;
|
||||
|
||||
|
@ -574,7 +580,14 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
|
|||
GSMimeHeader *h;
|
||||
|
||||
h = [[GSMimeHeader alloc] initWithName: key value: val parameters: nil];
|
||||
[buf appendData: [h rawMimeDataPreservingCase: YES foldedAt: 0]];
|
||||
if (debug || masked)
|
||||
{
|
||||
[h addToBuffer: buf masking: &masked];
|
||||
}
|
||||
else
|
||||
{
|
||||
[h addToBuffer: buf masking: NULL];
|
||||
}
|
||||
RELEASE(h);
|
||||
}
|
||||
NSEndMapTableEnumeration(&enumerator);
|
||||
|
@ -603,11 +616,15 @@ debugWrite(GSHTTPURLHandle *handle, NSData *data)
|
|||
*/
|
||||
if (debug)
|
||||
{
|
||||
if (NO == [ioDelegate putBytes: [buf bytes]
|
||||
ofLength: [buf length]
|
||||
if (nil == masked)
|
||||
{
|
||||
masked = buf; // Just log unmasked data
|
||||
}
|
||||
if (NO == [ioDelegate putBytes: [masked bytes]
|
||||
ofLength: [masked length]
|
||||
byHandle: self])
|
||||
{
|
||||
debugWrite(self, buf);
|
||||
debugWrite(self, masked);
|
||||
}
|
||||
}
|
||||
[sock writeInBackgroundAndNotify: buf];
|
||||
|
|
|
@ -47,6 +47,11 @@
|
|||
#import "GNUstepBase/NSString+GNUstepBase.h"
|
||||
#import "GNUstepBase/NSURL+GNUstepBase.h"
|
||||
|
||||
@interface GSMimeHeader (HTTPRequest)
|
||||
- (void) addToBuffer: (NSMutableData*)buf
|
||||
masking: (NSMutableData**)masked;
|
||||
@end
|
||||
|
||||
/* Define to 1 for experimental (net yet working) compression support
|
||||
*/
|
||||
#ifdef USE_ZLIB
|
||||
|
@ -355,8 +360,9 @@ static NSLock *pairLock = nil;
|
|||
float _version; // The HTTP version in use.
|
||||
int _statusCode; // The HTTP status code returned.
|
||||
NSInputStream *_body; // for sending the body
|
||||
unsigned _writeOffset; // Request data to write
|
||||
NSData *_writeData; // Request bytes written so far
|
||||
unsigned _writeOffset; // Request bytes written so far
|
||||
NSData *_writeData; // Request data to write
|
||||
NSData *_masked; // Request masked data
|
||||
BOOL _complete;
|
||||
BOOL _debug;
|
||||
BOOL _isLoading;
|
||||
|
@ -780,6 +786,8 @@ typedef struct {
|
|||
[_body release]; // for sending the body
|
||||
[_response release];
|
||||
[_credential release];
|
||||
DESTROY(_writeData);
|
||||
DESTROY(_masked);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
@ -1002,6 +1010,7 @@ typedef struct {
|
|||
}
|
||||
_isLoading = NO;
|
||||
DESTROY(_writeData);
|
||||
DESTROY(_masked);
|
||||
if (this->input != nil)
|
||||
{
|
||||
[this->input setDelegate: nil];
|
||||
|
@ -1533,17 +1542,24 @@ typedef struct {
|
|||
{
|
||||
if (_debug)
|
||||
{
|
||||
if (NO == [_logDelegate putBytes: bytes + _writeOffset
|
||||
const unsigned char *b = [_masked bytes];
|
||||
|
||||
if (NULL == b)
|
||||
{
|
||||
b = bytes;
|
||||
}
|
||||
if (NO == [_logDelegate putBytes: b + _writeOffset
|
||||
ofLength: written
|
||||
byHandle: self])
|
||||
{
|
||||
debugWrite(self, written, bytes + _writeOffset);
|
||||
debugWrite(self, written, b + _writeOffset);
|
||||
}
|
||||
}
|
||||
_writeOffset += written;
|
||||
if (_writeOffset >= len)
|
||||
{
|
||||
DESTROY(_writeData);
|
||||
DESTROY(_masked);
|
||||
if (_body == nil)
|
||||
{
|
||||
_body = RETAIN([this->request HTTPBodyStream]);
|
||||
|
@ -1710,6 +1726,7 @@ typedef struct {
|
|||
case NSStreamEventOpenCompleted:
|
||||
{
|
||||
NSMutableData *m;
|
||||
NSMutableData *mm = nil;
|
||||
NSDictionary *d;
|
||||
NSEnumerator *e;
|
||||
NSString *s;
|
||||
|
@ -1733,6 +1750,7 @@ typedef struct {
|
|||
[stream propertyForKey: GSStreamRemoteAddressKey],
|
||||
[stream propertyForKey: GSStreamRemotePortKey]];
|
||||
DESTROY(_writeData);
|
||||
DESTROY(_masked);
|
||||
_writeOffset = 0;
|
||||
if ([this->request HTTPBodyStream] == nil)
|
||||
{
|
||||
|
@ -1783,8 +1801,14 @@ typedef struct {
|
|||
h = [[GSMimeHeader alloc] initWithName: s
|
||||
value: [d objectForKey: s]
|
||||
parameters: nil];
|
||||
[m appendData:
|
||||
[h rawMimeDataPreservingCase: YES foldedAt: 0]];
|
||||
if (_debug || mm)
|
||||
{
|
||||
[h addToBuffer: m masking: &mm];
|
||||
}
|
||||
else
|
||||
{
|
||||
[h addToBuffer: m masking: NULL];
|
||||
}
|
||||
RELEASE(h);
|
||||
}
|
||||
|
||||
|
@ -1839,7 +1863,8 @@ typedef struct {
|
|||
[m appendData: [s dataUsingEncoding: NSASCIIStringEncoding]];
|
||||
}
|
||||
[m appendBytes: "\r\n" length: 2]; // End of headers
|
||||
_writeData = m;
|
||||
_writeData = m;
|
||||
ASSIGN(_masked, mm);
|
||||
} // Fall through to do the write
|
||||
|
||||
case NSStreamEventHasSpaceAvailable:
|
||||
|
|
Loading…
Reference in a new issue