Changes to avoid placing sensitive information in debug logs.

This commit is contained in:
Richard Frith-Macdonald 2022-05-20 12:32:31 +01:00
parent cc38f2f4a1
commit 1934ce6205
4 changed files with 212 additions and 18 deletions

View file

@ -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

View file

@ -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
{

View file

@ -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];

View file

@ -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: