From 78f77d7dd36cf6928115df3b30ed7c6384bc6f5e Mon Sep 17 00:00:00 2001
From: william-stacken
Date: Fri, 6 Aug 2021 13:12:18 +0200
Subject: [PATCH] log file for exceptions, delete all logs, disable stdout
---
.../TiriryaraiMitm/Properties/AssemblyInfo.cs | 2 +-
.../TuxEverywhere/Properties/AssemblyInfo.cs | 2 +-
README.md | 2 +
Tiriryarai/Http/HttpMessage.cs | 2 +-
Tiriryarai/Program.cs | 28 ++---
Tiriryarai/Server/HttpsMitmProxy.cs | 98 +++++++++++++----
Tiriryarai/Util/HttpsMitmProxyCache.cs | 5 +-
Tiriryarai/Util/HttpsMitmProxyConfig.cs | 30 +++--
Tiriryarai/Util/Logger.cs | 104 ++++++++----------
Tiriryarai/Util/Resources.cs | 13 ++-
10 files changed, 171 insertions(+), 115 deletions(-)
diff --git a/Plugins/TiriryaraiMitm/Properties/AssemblyInfo.cs b/Plugins/TiriryaraiMitm/Properties/AssemblyInfo.cs
index c1f1a0a..42d8bcc 100644
--- a/Plugins/TiriryaraiMitm/Properties/AssemblyInfo.cs
+++ b/Plugins/TiriryaraiMitm/Properties/AssemblyInfo.cs
@@ -39,7 +39,7 @@
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-[assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.1.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
diff --git a/Plugins/TuxEverywhere/Properties/AssemblyInfo.cs b/Plugins/TuxEverywhere/Properties/AssemblyInfo.cs
index 0a9a9b8..318a832 100644
--- a/Plugins/TuxEverywhere/Properties/AssemblyInfo.cs
+++ b/Plugins/TuxEverywhere/Properties/AssemblyInfo.cs
@@ -38,7 +38,7 @@
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-[assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.1.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
diff --git a/README.md b/README.md
index 994bcf4..217dd30 100644
--- a/README.md
+++ b/README.md
@@ -38,6 +38,8 @@ The program creates the following folders and files:
on an array of "log entries", where each log entry has a 16 byte initialization vector field,
a 4 byte "length" field, followed by a "length" byte encrypted HTML representation of
a HTTP message. Custom objects can also be logged by the custom plugins.
+ - `-Debug-.tirlog`: Log file that mainly contains stack traces and other useful information
+ for troubleshooting.
- `-RootCA-.pfx`: PKCS12 file containing the Root CA certificate that will be used to sign
certificates generated by Tiriryarai. The Root CA certificate needs to be
installed in your client, refer to [6. How To Use](#6-how-to-use) for details.
diff --git a/Tiriryarai/Http/HttpMessage.cs b/Tiriryarai/Http/HttpMessage.cs
index 3f62d25..bb0166f 100644
--- a/Tiriryarai/Http/HttpMessage.cs
+++ b/Tiriryarai/Http/HttpMessage.cs
@@ -552,7 +552,7 @@ protected Dictionary ParseUrlEncoded(string urlEncoded)
else if (i > 0)
{
key = keyVal.Substring(0, i).ToLower();
- val = keyVal.Substring(i + 1);
+ val = HttpUtility.UrlDecode(keyVal.Substring(i + 1));
}
else
{
diff --git a/Tiriryarai/Program.cs b/Tiriryarai/Program.cs
index 03e7a86..cd5ab4d 100644
--- a/Tiriryarai/Program.cs
+++ b/Tiriryarai/Program.cs
@@ -45,6 +45,7 @@ static void Main(string[] args)
HttpsMitmProxy proxy = null;
List extraOpts = null;
Dictionary props = new Dictionary();
+ Logger logger = Logger.GetSingleton();
try
{
HttpsMitmProxyConfig conf = HttpsMitmProxyConfig.GetSingleton();
@@ -96,34 +97,33 @@ static void Main(string[] args)
Environment.Exit(-1);
}
- PrintStartup();
+ if (!conf.DisableStdout)
+ PrintStartup();
if (!conf.Authenticate)
{
- Console.WriteLine("NOTICE: Authentication for accessing admin pages is disabled.");
- Console.WriteLine("Hosting Tiriryarai on the public internet or an untrusted network is strongly discouraged.");
- Console.WriteLine("If this was unintentional, see the help by using the \"-h\" flag.");
- Console.WriteLine();
+ logger.WriteStdout("NOTICE: Authentication for accessing admin pages is disabled.");
+ logger.WriteStdout("Hosting Tiriryarai on the public internet or an untrusted network is strongly discouraged.");
+ logger.WriteStdout("If this was unintentional, see the help by using the \"-h\" flag.\n");
}
- Console.Write("Starting server and generating certificates... ");
+ logger.WriteStdout("Starting server and generating certificates... ");
proxy = new HttpsMitmProxy(conf);
- Console.WriteLine("Done");
- Console.WriteLine();
- Console.WriteLine("Tiriryarai has started!");
- Console.WriteLine("Configure your client to use host " + conf.Hostname + " and port " + conf.Port + " as a HTTP proxy.");
- Console.WriteLine("Then open http://" + Resources.HOSTNAME + " for more information.");
+ logger.WriteStdout("Done\n");
+ logger.WriteStdout("Tiriryarai has started!");
+ logger.WriteStdout("Configure your client to use host " + conf.Hostname + " and port " + conf.Port + " as a HTTP proxy.");
+ logger.WriteStdout("Then open http://" + Resources.HOSTNAME + " for more information.");
}
catch (Exception e)
{
if (e is TargetInvocationException t)
e = t.InnerException;
- Console.WriteLine("\nFailed to initialize server:");
- Console.WriteLine(e.Message);
+ logger.WriteStdout("Failed to initialize server:");
+ logger.WriteStdout(e.Message);
Environment.Exit(-2);
}
proxy.Start();
- Console.WriteLine("Tiriryarai shut down...");
+ logger.WriteStdout("Tiriryarai shut down...");
}
private static void PrintStartup()
diff --git a/Tiriryarai/Server/HttpsMitmProxy.cs b/Tiriryarai/Server/HttpsMitmProxy.cs
index e1612db..00b440e 100755
--- a/Tiriryarai/Server/HttpsMitmProxy.cs
+++ b/Tiriryarai/Server/HttpsMitmProxy.cs
@@ -107,6 +107,11 @@ public HttpsMitmProxy(HttpsMitmProxyConfig conf)
}
}},
{"favicon.ico", (req, resp) => {
+ if (!string.Empty.Equals(req.SubPath(1)))
+ {
+ DefaultNotFound(resp, req);
+ return;
+ }
switch (req.Method)
{
case Method.HEAD:
@@ -129,6 +134,11 @@ public HttpsMitmProxy(HttpsMitmProxyConfig conf)
logger.Log(15, req.Host, "OUTGOING INTERNAL RESPONSE", resp);
}},
{"cert", (req, resp) => {
+ if (!string.Empty.Equals(req.SubPath(1)))
+ {
+ DefaultNotFound(resp, req);
+ return;
+ }
switch (req.Method)
{
case Method.HEAD:
@@ -154,6 +164,11 @@ public HttpsMitmProxy(HttpsMitmProxyConfig conf)
}},
{Resources.CA_ISSUER_PATH, (req, resp) =>
{
+ if (!string.Empty.Equals(req.SubPath(1)))
+ {
+ DefaultNotFound(resp, req);
+ return;
+ }
logger.Log(8, req.Host, "INCOMMING ISSUER REQUEST", req);
switch (req.Method)
{
@@ -179,6 +194,11 @@ public HttpsMitmProxy(HttpsMitmProxyConfig conf)
}},
{Resources.OCSP_PATH, (req, resp) =>
{
+ if (!string.Empty.Equals(req.SubPath(1)))
+ {
+ DefaultNotFound(resp, req);
+ return;
+ }
logger.Log(8, req.Host, "INCOMMING OCSP REQUEST", req);
switch (req.Method)
{
@@ -196,7 +216,8 @@ public HttpsMitmProxy(HttpsMitmProxyConfig conf)
}
catch (Exception e)
{
- logger.LogException(e, req);
+ logger.LogDebug(10, e);
+ logger.LogDebug(10, req);
}
X509OCSPResponse ocspResp = ocspReq != null ?
cache.GetOCSPResponse(ocspReq) :
@@ -223,6 +244,11 @@ public HttpsMitmProxy(HttpsMitmProxyConfig conf)
}},
{Resources.CRL_PATH, (req, resp) =>
{
+ if (!string.Empty.Equals(req.SubPath(1)))
+ {
+ DefaultNotFound(resp, req);
+ return;
+ }
logger.Log(8, req.Host, "INCOMMING CRL REQUEST", req);
switch (req.Method)
{
@@ -255,7 +281,7 @@ public HttpsMitmProxy(HttpsMitmProxyConfig conf)
DateTime? ifModified = req.GetDateHeader("If-Modified-Since");
string logFile = req.SubPath(1);
- if ("".Equals(logFile))
+ if (string.Empty.Equals(logFile))
{
// Request to log directory
switch (req.Method)
@@ -281,15 +307,35 @@ public HttpsMitmProxy(HttpsMitmProxyConfig conf)
string.Format(Resources.LOG_PAGE, entryBuilder)
), false, req);
return;
+ case Method.POST:
+ if (!"application/x-www-form-urlencoded".Equals(req.ContentTypeWithoutCharset))
+ {
+ DefaultBadMediaType(resp, req);
+ return;
+ }
+ else if ("on".Equals(req.GetBodyParam("sure")) && "Delete All".Equals(req.GetBodyParam("deleteall")))
+ {
+ foreach (string log in logger.LogNames)
+ logger.DeleteLog(log);
+ }
+ break;
+ case Method.DELETE:
+ foreach (string log in logger.LogNames)
+ logger.DeleteLog(log);
+ break;
case Method.OPTIONS:
- DefaultOptions(resp, req);
+ DefaultOptions(resp, req, Method.POST, Method.DELETE);
return;
default:
DefaultUnsupported(resp, req);
return;
}
+ resp.Status = 303;
+ resp.SetHeader("Location", "/logs");
+ resp.ContentLength = 0;
+ return;
}
- else if (logger.Exists(logFile) && "".Equals(req.SubPath(2))) // Only one path level allowed
+ else if (logger.Exists(logFile) && string.Empty.Equals(req.SubPath(2))) // Only one path level allowed
{
switch (req.Method)
{
@@ -339,7 +385,7 @@ public HttpsMitmProxy(HttpsMitmProxyConfig conf)
DefaultNotFound(resp, req);
}},
{"config", (req, resp) => {
- if (conf.Configuration)
+ if (conf.Configuration && string.Empty.Equals(req.SubPath(1)))
{
logger.Log(8, req.Host, "INCOMMING CONFIG REQUEST", req);
string value, description;
@@ -413,7 +459,7 @@ public HttpsMitmProxy(HttpsMitmProxyConfig conf)
}
catch (Exception e)
{
- logger.LogException(e);
+ logger.LogDebug(5, e);
success = false;
}
if (clearCache)
@@ -521,14 +567,6 @@ private void ProcessClient(TcpClient client)
}
catch (Exception e)
{
- if (e is IOException ||
- e is ObjectDisposedException ||
- e.InnerException is IOException)
- {
- // Connection has become inactive or was closed by the remote
- keepAlive = false;
- break;
- }
resp = DefaultHttpResponse(400);
resp.ToStream(stream);
throw e;
@@ -582,14 +620,17 @@ e is ObjectDisposedException ||
catch (Exception e)
{
if (e is IOException ||
+ e is SocketException ||
e is ObjectDisposedException ||
- e.InnerException is IOException)
+ e.InnerException is IOException ||
+ e is AggregateException)
{
// Connection has become inactive or was closed by the remote
+ logger.LogDebug(12, e);
keepAlive = false;
break;
}
- logger.LogException(e);
+ logger.LogDebug(8, e);
resp = DefaultHttpResponse(400);
}
resp.ToStream(sslStream);
@@ -599,7 +640,7 @@ e is ObjectDisposedException ||
}
catch (Exception e)
{
- logger.LogException(e);
+ logger.LogDebug(13, e);
}
finally
{
@@ -638,7 +679,18 @@ e is ObjectDisposedException ||
}
catch (Exception e)
{
- logger.LogException(e);
+ if (e is IOException ||
+ e is SocketException ||
+ e is ObjectDisposedException ||
+ e.InnerException is IOException)
+ {
+ // Connection was probably closed by the remote
+ logger.LogDebug(15, e);
+ }
+ else
+ {
+ logger.LogDebug(8, e);
+ }
}
finally
{
@@ -652,8 +704,8 @@ private HttpResponse HandleRequest(HttpRequest req, HttpsClient destination, boo
HttpMessage http;
try
{
- Console.WriteLine("\n--------------------\n" +
- req.Method + (tls ? " https://" : " http://") + destination.HostnameWithPort + req.Path);
+ logger.WriteStdout("\n--------------------\n" +
+ req.Method + (tls ? " https://" : " http://") + destination.HostnameWithPort);
if (!conf.MitM.Block(destination.Hostname))
{
logger.Log(3, destination.Hostname, "RECEIVED REQUEST", req);
@@ -673,7 +725,7 @@ private HttpResponse HandleRequest(HttpRequest req, HttpsClient destination, boo
if (e is IOException || e is SocketException)
return DefaultHttpResponse(504);
- logger.LogException(e);
+ logger.LogDebug(6, e);
return DefaultHttpResponse(502);
}
if (modified.HeaderContains("Connection", "close") || resp.HeaderContains("Connection", "close"))
@@ -702,7 +754,7 @@ private HttpResponse HandleRequest(HttpRequest req, HttpsClient destination, boo
}
catch (Exception e)
{
- logger.LogException(e);
+ logger.LogDebug(2, e);
resp = DefaultHttpResponse(500, req);
}
return resp;
@@ -799,7 +851,7 @@ private HttpResponse HomePage(HttpRequest req, string host, IPAddress client, bo
}
catch (Exception e)
{
- logger.LogException(e);
+ logger.LogDebug(2, e);
resp = DefaultHttpResponse(500, req);
}
return resp;
diff --git a/Tiriryarai/Util/HttpsMitmProxyCache.cs b/Tiriryarai/Util/HttpsMitmProxyCache.cs
index 79fd47f..d84bb90 100644
--- a/Tiriryarai/Util/HttpsMitmProxyCache.cs
+++ b/Tiriryarai/Util/HttpsMitmProxyCache.cs
@@ -204,7 +204,7 @@ public X509Certificate2 GetRootCA()
return AddOrGetExisting(rootCA, certPath => {
if (!(certPath is string path))
throw new ArgumentException("certPath must be a string");
- Console.WriteLine("\n--------------------\nNOTICE: The root CA certificate has expired and will be replaced." +
+ logger.LogDebug(1, "NOTICE: The root CA certificate has expired and will be replaced." +
"Please install the new certificate and remove the old one.");
// TODO Clear the cache somehow since essentially everything in the cache is now invalid.
File.Delete(path);
@@ -558,7 +558,8 @@ private X509OCSPResponse CreateOCSPResponse(object id)
}
catch (Exception e)
{
- logger.LogException(e, certId);
+ logger.LogDebug(10, e);
+ logger.LogDebug(10, certId);
ocsp = new X509OCSPResponse(X509OCSPResponse.ResponseStatus.MalformedRequest);
}
return new X509OCSPResponse(ocsp.Sign(ca));
diff --git a/Tiriryarai/Util/HttpsMitmProxyConfig.cs b/Tiriryarai/Util/HttpsMitmProxyConfig.cs
index 9cd48b1..b6874ae 100644
--- a/Tiriryarai/Util/HttpsMitmProxyConfig.cs
+++ b/Tiriryarai/Util/HttpsMitmProxyConfig.cs
@@ -164,10 +164,10 @@ public byte[] PassKey
[HttpsMitmProxy(HttpsMitmProxyProperty.Password | HttpsMitmProxyProperty.Authentication | HttpsMitmProxyProperty.Cache,
"password", "w|password=")]
[Description("The password required for accessing the admin pages if one should be required. " +
- "It will be sent securely using HTTPS only. NOTICE: If this password is changed " +
- "Tiriryarai will be unable to read any existing logs. If that is a concern, please back up the logs " +
- "first. Furthermore, if updated at runtime, the cache will be cleared, meaning that you must re-install " +
- "the root certificate.")]
+ "It will be sent securely using HTTPS only and can only be set if the username is set. " +
+ "NOTICE: If this password is changed, Tiriryarai will be unable to read any existing " +
+ "logs. If that is a concern, please back up the logs first. Furthermore, if updated at runtime, the " +
+ "cache will be cleared, meaning that you must re-install the root certificate.")]
public string Password
{
set
@@ -196,8 +196,9 @@ public byte[] ProxyPassKey
}
[HttpsMitmProxy(HttpsMitmProxyProperty.Password | HttpsMitmProxyProperty.Standard, "password", "x|proxy-pass=")]
- [Description("The password required for using the proxy if one should be required. " +
- "It will be sent insecurely using HTTP and SHOULD NOT be the same as the admin password.")]
+ [Description("The password required for using the proxy if one should be required. It will be sent " +
+ "insecurely using HTTP and can only be set if the username is set. Also, it SHOULD NOT " +
+ "be the same as the admin password.")]
public string ProxyPassword
{
set
@@ -326,11 +327,7 @@ public bool Configuration
}
private bool certignore;
- ///
- /// Gets or sets a value indicating whether invalid certificates should be ignored
- /// by the HttpsClient.
- ///
- /// true if certificates should be ignored; otherwise, false.
+
[HttpsMitmProxy(HttpsMitmProxyProperty.Standard, "checkbox", "i|ignore-certs")]
[Description("Ignore invalid certificates when sending HTTPS requests.")]
public bool IgnoreCertificates
@@ -455,6 +452,17 @@ public uint CachePollingInterval
}
}
+ private bool disablestdout;
+
+ [HttpsMitmProxy(HttpsMitmProxyProperty.Standard, "checkbox", "q|quiet")]
+ [Description("Do not print anything to standard out, such as incomming requests " +
+ "or status information.")]
+ public bool DisableStdout
+ {
+ get { return disablestdout; }
+ set { disablestdout = value; LastModifiedTime = DateTime.UtcNow; }
+ }
+
[HttpsMitmProxy(HttpsMitmProxyProperty.None, null, null)]
[Description("A flag indicating that Tiriryarai is undergoing maintenance. This could for " +
"example happen due to the cache being cleared or another resource heavy operation.")]
diff --git a/Tiriryarai/Util/Logger.cs b/Tiriryarai/Util/Logger.cs
index f20d3f1..7932fa1 100644
--- a/Tiriryarai/Util/Logger.cs
+++ b/Tiriryarai/Util/Logger.cs
@@ -375,6 +375,33 @@ public void Log(uint level, string logname, string head, object obj)
Task.Run(() => LogInternal(level, logname, head, obj));
}
+ ///
+ /// Logs an object to the debug log.
+ ///
+ /// The log level to use for the object.
+ /// Information object to log.
+ public void LogDebug(uint level, object info)
+ {
+ if (level < 1 || conf.LogVerbosity < level)
+ return;
+ string logname = "-Debug-";
+ string head = "DEBUG";
+ ThrowIfInvalid(level, logname, head, info);
+
+ Task.Run(() => LogInternal(level, logname, head, info));
+ }
+
+ ///
+ /// Prints an object to standard out in case it is allowed by the
+ /// configuration.
+ ///
+ /// object to print.
+ public void WriteStdout(object o)
+ {
+ if (!conf.DisableStdout)
+ Console.WriteLine(o);
+ }
+
private void LogInternal(uint level, string logname, string head, object obj)
{
int attempts = 5;
@@ -420,49 +447,9 @@ private void LogInternal(uint level, string logname, string head, object obj)
}
}
}
- catch (Exception e)
- {
- LogException(e);
- }
- }
-
- ///
- /// Logs an exception to stdout.
- ///
- /// The exception.
- public void LogException(Exception e)
- {
- LogException(e, null);
- }
-
- ///
- /// Logs an exception to stdout.
- ///
- /// The exception.
- /// Optional information object to log.
- public void LogException(Exception e, object info)
- {
- switch (conf.LogVerbosity)
+ catch
{
- case 0:
- case 1:
- case 2:
- case 3:
- break;
- case 4:
- case 5:
- case 6:
- case 7:
- Console.WriteLine(e.Message);
- if (info != null)
- Console.WriteLine(info);
- break;
- default:
- Console.WriteLine(e);
- if (info != null)
- Console.WriteLine(info);
- break;
-
+ WriteStdout("\n--------------------\nWARNING: Request to log timed out.");
}
}
@@ -476,7 +463,7 @@ public string[] LogNames
files[i] = Path.GetFileName(files[i]);
files[i] = files[i].Substring(0, files[i].Length - LOG_SUFFIX.Length);
}
- Array.Sort(files, StringComparer.InvariantCulture);
+ Array.Sort(files, StringComparer.Ordinal);
return files;
}
}
@@ -555,27 +542,26 @@ public byte[] ReadLog(string logname)
{
for (int i = 0; i < attempts; i++)
{
- try
+ using (MemoryStream ms = new MemoryStream())
{
- using (MemoryStream ms = new MemoryStream())
+ FileStream s = null;
+ try
{
- using (var s = new FileStream(path, FileMode.Open))
- {
- using (var logStream = new LogStream(s, conf.PassKey, mode: true))
- {
- logStream.CopyTo(ms);
- return ms.ToArray();
- }
- }
+ s = new FileStream(path, FileMode.Open);
+ }
+ catch
+ {
+ Thread.Sleep(100);
+ continue;
+ }
+ using (var logStream = new LogStream(s, conf.PassKey, mode: true))
+ {
+ logStream.CopyTo(ms);
+ return ms.ToArray();
+ // s will be closed here since leaveOpen in LogStream is false by default
}
- }
- catch (Exception e)
- {
- LogException(e);
- Thread.Sleep(100);
}
}
- throw new IOException("Failed to read the log");
}
throw new FileNotFoundException(logname);
}
diff --git a/Tiriryarai/Util/Resources.cs b/Tiriryarai/Util/Resources.cs
index 92ea0e7..ea8aa93 100644
--- a/Tiriryarai/Util/Resources.cs
+++ b/Tiriryarai/Util/Resources.cs
@@ -185,7 +185,14 @@ public static Version Version
"Actions | " +
"" +
"{0}" +
- ""
+ "" +
+ "" +
+ "" +
+ "
"
);
public static string LOG_ENTRY =
@@ -209,10 +216,10 @@ public static Version Version
"be reset to their default value. Note that when the form is submitted, invalid " +
"fields will be ignored, but valid fields will still update the configuration." +
"
" +
- "" +
+ "
" +
"Much of the configuration CANNOT be reverted once updated. Please read through the " +
"information carefully before making changes and make sure you know what you are doing." +
- "
" +
+ "" +
"