1 module proxy;
2 
3 import std.stdio;
4 import std.functional;
5 import std.format;
6 import std.string;
7 
8 import libssh.session;
9 import libssh.channel;
10 import libssh.errors;
11 import libssh.server;
12 import libssh.poll;
13 
14 enum auto USER = "myuser";
15 enum auto PASSWORD = "mypassword";
16 
17 bool authentificated = false;
18 int tries = 0;
19 bool error = false;
20 SSHChannel chan = null;
21 string username;
22 GSSAPICreds clientCreds = 0;
23 
24 AuthState authPassword(SSHSession session, string user, string password) {
25     stdout.writefln("Authenticating user %s pwd %s\n",user, password);
26     if (user == USER && password == PASSWORD) {
27         authentificated = true;
28         stdout.writefln("Authenticated");
29         return AuthState.Success;
30     }
31     if (tries >= 3) {
32         stdout.writefln("Too many authentication tries");
33         session.disconnect();
34         error = true;
35         return AuthState.Denied;
36     }
37     tries++;
38     return AuthState.Denied;
39 }
40 
41 version(LIBSSH_WITH_GSSAPI) {
42     AuthState authGSSAPIMic(SSHSession session, string user, string principal) {
43         clientCreds = session.gssapiGetCreds;
44         stdout.writefln("Authenticating user %s with gssapi principal %s",user, principal);
45         if (clientCreds !is null) {
46             stdout.writefln("Received some gssapi credentials");
47         } else {
48             stdout.writefln("Not received any forwardable creds");
49         }
50         stdout.writefln("authenticated");
51         authentificated = true;
52         username = principal.dup;
53         return AuthState.Success;
54     }
55 }
56 
57 bool ptyRequest(SSHChannel channel, string term, int width, int height, int pxWidth, int pxHeight) {
58     stdout.writeln("allocated terminal");
59     return true;
60 }
61 
62 bool shellRequest(SSHChannel channel) {
63     stdout.writeln("allocated shell");
64     return true;
65 }
66 
67 SSHChannel newSessionChannel(SSHSession session) {
68     if (chan !is null) {
69         return null;
70     }
71     chan = session.newChannel();
72     chan.onPtyRequestCallback = toDelegate(&ptyRequest);
73     chan.onShellRequestCallback = toDelegate(&shellRequest);
74     return chan;
75 }
76 
77 // TODO: options parsing
78 
79 int main(string[] argv) {
80     scope(exit) sshFinalize();
81 
82     try {
83         auto sshBind = new SSHBind();
84         scope(exit) sshBind.dispose();
85 
86         auto session = new SSHSession();
87         scope(exit) session.dispose();
88         
89         sshBind.rsaKey = "sshd_rsa";
90         
91         // TODO: options parsing
92         sshBind.bindPort = 2222;
93 
94         sshBind.listen();
95 
96         sshBind.accept(session);
97 
98         session.serverAuthPasswordCallback = toDelegate(&authPassword);
99         session.serverChannelOpenRequestCallback = toDelegate(&newSessionChannel);
100         version(LIBSSH_WITH_GSSAPI) {
101             session.ServerAuthGSSAPIMicCallback = toDelegate(&authGSSAPIMic);
102         }
103 
104         session.handleKeyExchange();
105         version(LIBSSH_WITH_GSSAPI) {
106             session.authMethods = AuthMethod.Password | AuthMethod.GSSAPIMic;
107         } else {
108             session.authMethods = AuthMethod.Password;
109         }
110 
111         auto mainLoop = new SSHEvent();
112         scope(exit) mainLoop.dispose();
113         mainLoop.addSession(session);
114 
115         while (!(authentificated && chan !is null)) {
116             if (error) {
117                 break;
118             }
119 
120             mainLoop.doPoll(-1);
121         }
122 
123         if (error) {
124             stdout.writeln("Error, exiting loop");
125             return 1;
126         } else {
127             stdout.writeln("Authenticated and got a channel");
128         }
129         if (clientCreds == 0) {
130             chan.write("Sorry, but you do not have forwardable tickets. Try again with -K\r\n");
131             stdout.writeln("Sorry, but you do not have forwardable tickets. Try again with -K");
132             session.disconnect();
133             return 1;
134         }
135 
136         chan.write(format("Hello %s, welcome to the Sample SSH proxy.\r\nPlease select your destination: ",
137                 username));
138 
139         string host = "";
140         int i = 0;
141         do {
142             char[2048] buf;
143             i = chan.read(buf, false);
144             if (i > 0) {
145                 chan.write(buf[0 .. i]);
146                 if (host.length + i < 128) {
147                     host ~= buf[0 .. i];
148                 }
149                 auto lfIndex = host.indexOf('\x0d');
150                 if (lfIndex >= 0) {
151                     host = host[0 .. lfIndex];
152                     chan.write("\n");
153                     break;
154                 }
155             } else {
156                 stdout.writefln("Error: %s", session.lastError);
157                 return 1;
158             }
159         } while (i > 0);
160 
161         auto buf = format("Trying to connect to \"%s\"\r\n", host);
162         chan.write(buf);
163         stdout.write(buf);
164 
165         auto clientSession = new SSHSession();
166         scope(exit) clientSession.dispose();
167 
168         /* ssh servers expect username without realm */
169         auto ptr = username.indexOf('@');
170         if (ptr >= 0) {
171             username = username[0 .. ptr];
172         }
173 
174         clientSession.host = host;
175         clientSession.user = username;
176         version(LIBSSH_WITH_GSSAPI) {
177             clientSession.gssapiCreds = clientCreds;
178         }
179         clientSession.connect();
180 
181         auto rc = clientSession.userauthNone(null);
182         if (rc == AuthState.Success) {
183             stdout.writeln("Authenticated using method none");
184         } else {
185             rc = clientSession.userauthGSSAPI();
186             if (rc != AuthState.Success) {
187                 stdout.writefln("GSSAPI Authentication failed: %s", clientSession.lastError);
188                 return 1;
189             }
190         }
191 
192         buf = "Authentication success\r\n";
193         stdout.write(buf);
194         chan.write(buf);
195         clientSession.disconnect();
196         session.disconnect();
197 
198     } catch (SSHException sshException) {
199         stderr.writefln("SSH exception. Code = %d, Message:\n%s\n",
200             sshException.errorCode, sshException.msg);
201         return -1;
202     }
203     return 0;
204 }