1 module client;
2 version(Windows):
3 import sspi;
4 import std.socket : Socket;
5 import core.sys.windows.ntsecpkg : SECURITY_NATIVE_DREP;
6 
7 // Port of MS example of client-side program to establish an SSPI socket connection with a server and exchange messages.
8 
9 
10 //  Define macros and constants.
11 enum BIG_BUFF = 2048;
12 
13 bool SEC_SUCCESS(T)(T status)
14 {
15 	return status >= 0;
16 }
17 
18 enum g_usPort = 2000;
19 
20 enum cbMaxMessage = 12000;
21 enum MessageAttribute = IscReq.confidentiality;
22 
23 // #include <windows.h>
24 // #include <winsock.h>
25 // #include <stdio.h>
26 // #include <stdlib.h>
27 
28 int main(string[] args)
29 {
30 	import std.stdio : writefln, stderr;
31 	import std.exception : enforce;
32 	import std.format : format;
33 	import std.conv : to;
34 
35 	auto user = getUserName();
36 	auto client = ClientAuth("NTLM",user);
37 	string serverName = (args.length > 1) ? args[1] : "127.0.0.1";
38 	ushort serverPort = (args.length > 2) ? args[2].to!ushort : 9000;
39 
40     auto socket = client.createAuthenticatedSocket(serverName, serverPort);
41 
42     //--------------------------------------------------------------------
43     //   An authenticated session with a server has been established.
44     //   Receive and manage a message from the server.
45     //   First, find and display the name of the negotiated
46     //   SSP and the size of the signature and the encryption 
47     //   trailer blocks for this SSP.
48 
49 	auto securityPackageNegotiationInfo = queryContextAttributes!SecPkgContext_NegotiationInfoW(&client.context, SecPackageAttribute.negotiationInfo);
50 	writefln("Package Name: %s", securityPackageNegotiationInfo.packageInfo.Name);
51 
52     auto securityPackageAttrSizes = queryContextAttributes!SecPkgContext_Sizes(&client.context, SecPackageAttribute.sizes);
53 
54     auto cbMaxSignature = securityPackageAttrSizes.cbMaxSignature;
55     auto cbSecurityTrailer = securityPackageAttrSizes.cbSecurityTrailer;
56 
57     //--------------------------------------------------------------------
58     //   decrypt and display the message from the server.
59 
60     auto data = socket.receiveBytes();
61 
62     writefln("data before decryption including trailer (%s bytes):", data.length);
63     printHexDump(data);
64 	cbSecurityTrailer = (*(cast(ulong*) data.ptr)).to!uint;
65 	auto trailer = data[0 .. cbSecurityTrailer - 4];
66 	data = data [cbSecurityTrailer +4 .. $];
67 	auto message = client.decrypt(data.to!string, trailer.to!string);
68 
69     writefln("The message from the server is \n %s", message);
70 
71 	client.dispose();
72     return 0;
73 }
74 
75 //--------------------------------------------------------------------
76 //  connectAuthSocket establishes an authenticated socket connection 
77 //  with a server and initializes needed security package resources.
78 
79 Socket createAuthenticatedSocket(ref ClientAuth client, string serverName, ushort serverPort)
80 {
81 	import std.socket : getAddressInfo, AddressFamily, Socket, InternetAddress;
82 	import std.algorithm : filter;
83 	import std.array : array;
84 	import std.range : front;
85 
86     //--------------------------------------------------------------------
87     //  Lookup the server's address.
88 
89     auto addressInfos = getAddressInfo(serverName)
90 						.filter!(info => info.family == AddressFamily.INET)
91 						.array;
92 
93 	auto address = new InternetAddress(serverName, serverPort);
94     //--------------------------------------------------------------------
95     //  Create the socket.
96 
97     Socket socket = new Socket(addressInfos.front);
98 	socket.connect(address);	
99     auto message = client.genClientContext([]);
100     socket.sendMessage(message);
101 	message = socket.receiveMessage().idup;
102 	auto result = client.genClientContext(message);
103 	socket.sendMessage(result);
104     return socket;
105 }
106 
107 
108 auto genClientContext(ref ClientAuth client, const(ubyte)[] bufIn)
109 {
110 	import std.exception : enforce;
111 	import std.stdio : writefln;
112 	import std.conv : to;
113 
114 	auto result = client.authorize(bufIn);
115 	enforce(result[0] == SecurityStatus.okay, result.to!string);
116     writefln("Token buffer generated (%s bytes):", result[1].length);
117     printHexDump(result[1]);
118     return result[1];
119 }
120 
121 
122 
123 void printHexDump(const(ubyte)[] buf)
124 {
125 	import std.format : format;
126 	import std.stdio : writefln;
127 
128     size_t i,count,index;
129     char[] rgbDigits = "0123456789abcdef".dup;
130     char[100] rgbLine;
131     char cbLine;
132 	auto length = buf.length;
133 	char* buffer = cast(char*)buf.ptr;
134 
135     for(index = 0; length;
136         length -= count, buffer += count, index += count) 
137     {
138         count = (length > 16) ? 16:length;
139 
140         rgbLine = format!"%4.4x  "(index);
141         cbLine = 6;
142 
143         for(i=0;i<count;i++) 
144         {
145             rgbLine[cbLine++] = rgbDigits[buffer[i] >> 4];
146             rgbLine[cbLine++] = rgbDigits[buffer[i] & 0x0f];
147             if(i == 7) 
148             {
149                 rgbLine[cbLine++] = ':';
150             } 
151             else 
152             {
153                 rgbLine[cbLine++] = ' ';
154             }
155         }
156         for(; i < 16; i++) 
157         {
158             rgbLine[cbLine++] = ' ';
159             rgbLine[cbLine++] = ' ';
160             rgbLine[cbLine++] = ' ';
161         }
162 
163         rgbLine[cbLine++] = ' ';
164 
165         for(i = 0; i < count; i++) 
166         {
167             if(buffer[i] < 32 || buffer[i] > 126) 
168             {
169                 rgbLine[cbLine++] = '.';
170             } 
171             else 
172             {
173                 rgbLine[cbLine++] = buffer[i];
174             }
175         }
176 
177         rgbLine[cbLine++] = 0;
178         writefln("%s", rgbLine);
179     }
180 }
181 
182 void sendMessage(Socket socket, const(ubyte)[] message)
183 {
184 	import std.conv : to;
185 	auto messageLength = message.length.to!ulong;
186 	socket.sendBytes((cast(ubyte*)&messageLength)[0..4]);
187 	socket.sendBytes(message);
188 }
189 
190 ubyte[] receiveMessage(Socket socket)
191 {
192 	import std.conv : to;
193 	import std.exception : enforce;
194 
195 	ubyte[4] messageLengthBuf;
196 	enforce(socket.receive(messageLengthBuf) ==4);
197 	size_t messageLength = (*(cast(ulong*)(messageLengthBuf.ptr))).to!size_t;
198 	auto message = socket.receiveBytes(messageLength);
199 	return message;
200 }
201 
202 void sendBytes(Socket socket, const(ubyte)[] buf)
203 {
204     size_t numBytesRemaining = buf.length;
205 	size_t numBytesSent = 0;
206 
207     if (buf.length == 0)
208         return;
209 
210     while(numBytesRemaining > 0)
211     {
212         auto cbSent = socket.send(buf[numBytesSent .. $]);
213         numBytesSent += cbSent;
214         numBytesRemaining -= cbSent;
215     }
216 }
217 
218 ubyte[] receiveBytes(Socket socket, size_t messageLength = 0)
219 {
220 	import std.array : Appender;
221 	import std.conv : to;
222 
223 	Appender!(ubyte[]) ret;
224 	ubyte[1024] buf;
225     long cbRead, cbRemaining = messageLength.to!long;
226 
227     while(cbRemaining > 0)
228     {
229         cbRead = socket.receive(buf);
230 		ret.put(buf[0 .. cbRead.to!size_t]);
231         cbRemaining -= cbRead;
232     }
233     return ret.data;
234 }
235 
236