-
Notifications
You must be signed in to change notification settings - Fork 83
Expand file tree
/
Copy pathPlain.cs
More file actions
157 lines (135 loc) · 5.28 KB
/
Plain.cs
File metadata and controls
157 lines (135 loc) · 5.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Security;
using System.Text;
using System.Threading.Tasks;
using Waher.Security.LoginMonitor;
namespace Waher.Networking.SASL
{
/// <summary>
/// Authentication done by the PLAIN authentication mechanism.
/// https://tools.ietf.org/html/rfc4616
/// </summary>
public class Plain : AuthenticationMechanism
{
/// <summary>
/// Authentication done by the PLAIN authentication mechanism.
/// https://tools.ietf.org/html/rfc4616
/// </summary>
public Plain()
{
}
/// <summary>
/// Name of the mechanism.
/// </summary>
public override string Name => "PLAIN";
/// <summary>
/// Weight of mechanisms. The higher the value, the more preferred.
/// </summary>
public override int Weight => 0;
/// <summary>
/// Checks if a mechanism is allowed during the current conditions.
/// </summary>
/// <param name="SslStream">SSL stream, if available.</param>
/// <returns>If mechanism is allowed.</returns>
public override bool Allowed(SslStream SslStream)
{
return !(SslStream is null) && SslStream.IsEncrypted && SslStream.CipherStrength >= 128;
}
/// <summary>
/// Authentication request has been made.
/// </summary>
/// <param name="Data">Data in authentication request.</param>
/// <param name="Connection">Connection performing the authentication.</param>
/// <param name="PersistenceLayer">Persistence layer.</param>
/// <returns>If authentication was successful (true). If null, mechanism must send the corresponding challenge. If false, connection must close.</returns>
public override Task<bool?> AuthenticationRequest(string Data, ISaslServerSide Connection, ISaslPersistenceLayer PersistenceLayer)
{
if (string.IsNullOrEmpty(Data))
{
Connection.SaslChallenge(string.Empty);
return Task.FromResult<bool?>(null);
}
return this.ResponseRequest(Data, Connection, PersistenceLayer);
}
/// <summary>
/// Response request has been made.
/// </summary>
/// <param name="Data">Data in response request.</param>
/// <param name="Connection">Connection performing the authentication.</param>
/// <param name="PersistenceLayer">Persistence layer.</param>
/// <returns>If authentication was successful (true). If null, mechanism must send the corresponding error. If false, connection must close.</returns>
public override async Task<bool?> ResponseRequest(string Data, ISaslServerSide Connection, ISaslPersistenceLayer PersistenceLayer)
{
byte[] Bin = Convert.FromBase64String(Data);
string Request = Encoding.UTF8.GetString(Bin);
string[] Parts = Request.Split('\x00');
if (Parts.Length != 3)
{
await Connection.SaslErrorMalformedRequest();
LoginAuditor.Fail("Login attempt using malformed request.", string.Empty, Connection.RemoteEndPoint, Connection.Protocol);
return null;
}
string UserName = Parts[1];
string Password = Parts[2];
IAccount Account = await PersistenceLayer.GetAccount(UserName);
if (Account is null)
{
LoginAuditor.Fail("Login attempt using invalid user name.", UserName, Connection.RemoteEndPoint, Connection.Protocol,
new KeyValuePair<string, object>("UserName", UserName));
await Connection.SaslErrorNotAuthorized();
return null;
}
if (!Account.Enabled)
{
LoginAuditor.Fail("Login attempt using disabled account.", UserName, Connection.RemoteEndPoint, Connection.Protocol);
await Connection.SaslErrorAccountDisabled();
return null;
}
Connection.SetUserIdentity(UserName);
if (Password == Account.Password)
{
await Connection.SetAccount(Account);
Connection.ResetState(true);
await Connection.SaslSuccess(null);
LoginAuditor.Success("Login successful.", UserName, Connection.RemoteEndPoint, Connection.Protocol);
}
else
{
await Connection.SaslErrorNotAuthorized();
LoginAuditor.Fail("Login attempt failed.", UserName, Connection.RemoteEndPoint, Connection.Protocol);
}
return null;
}
/// <summary>
/// Performs intitialization of the mechanism. Can be used to set
/// static properties that will be used through-out the runtime of the
/// server.
/// </summary>
public override Task Initialize()
{
return Task.CompletedTask;
}
/// <summary>
/// Authenticates the user using the provided credentials.
/// </summary>
/// <param name="UserName">User Name</param>
/// <param name="Password">Password</param>
/// <param name="Connection">Connection</param>
/// <returns>If authentication was successful or not. If null is returned, the mechanism did not perform authentication.</returns>
public override async Task<bool?> Authenticate(string UserName, string Password, ISaslClientSide Connection)
{
using MemoryStream ms = new MemoryStream();
byte[] Bin = Encoding.UTF8.GetBytes(UserName);
ms.Write(Bin, 0, Bin.Length);
ms.WriteByte(0);
ms.Write(Bin, 0, Bin.Length);
ms.WriteByte(0);
Bin = Encoding.UTF8.GetBytes(Password);
ms.Write(Bin, 0, Bin.Length);
await Connection.Initiate(this, Convert.ToBase64String(ms.ToArray()));
return true;
}
}
}