Ticket #10266: super-patch

File super-patch, 72.4 KB (added by Ken Hornstein, 9 years ago)

Patch for GSSAPI/Kerberos support

Line 
1diff --git a/data/Makefile.am b/data/Makefile.am
2index 0bb621f..44542fa 100644
3--- a/data/Makefile.am
4+++ b/data/Makefile.am
5@@ -1,6 +1,6 @@
6 dist_noinst_DATA = install.nsi.in \
7 makezip.sh.in \
8- libgcc_s_sjlj-1.dll \
9+ libgcc_s_dw2-1.dll \
10 libstdc++-6.dll \
11 libwinpthread-1.dll \
12 nsis_appid.dll \
13diff --git a/data/install.nsi.in b/data/install.nsi.in
14index 529b94e..18f1f0e 100644
15--- a/data/install.nsi.in
16+++ b/data/install.nsi.in
17@@ -874,12 +874,12 @@ Section "FileZilla Client" SecMain
18 ; MinGW runtime libraries
19
20 ; First get rid of remnants
21- !insertmacro UnInstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "$INSTDIR\libgcc_s_sjlj-1.dll"
22+ !insertmacro UnInstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "$INSTDIR\libgcc_s_dw2-1.dll"
23 !insertmacro UnInstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "$INSTDIR\libstdc++-6.dll"
24 !insertmacro UnInstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "$INSTDIR\libwinpthread-1.dll"
25
26 ; Now install new version of runtime
27- !insertmacro InstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "${srcdir}\libgcc_s_sjlj-1.dll" "$INSTDIR\libgcc_s_sjlj-1.dll" "$INSTDIR"
28+ !insertmacro InstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "${srcdir}\libgcc_s_dw2-1.dll" "$INSTDIR\libgcc_s_dw2-1.dll" "$INSTDIR"
29 !insertmacro InstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "${srcdir}\libstdc++-6.dll" "$INSTDIR\libstdc++-6.dll" "$INSTDIR"
30 !insertmacro InstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "${srcdir}\libwinpthread-1.dll" "$INSTDIR\libwinpthread-1.dll" "$INSTDIR"
31
32@@ -1300,7 +1300,7 @@ Section "Uninstall"
33 Delete "$INSTDIR\fzputtygen.exe"
34
35 ; MinGW runtime libraries
36- !insertmacro UnInstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "$INSTDIR\libgcc_s_sjlj-1.dll"
37+ !insertmacro UnInstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "$INSTDIR\libgcc_s_dw2-1.dll"
38 !insertmacro UnInstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "$INSTDIR\libstdc++-6.dll"
39 !insertmacro UnInstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "$INSTDIR\libwinpthread-1.dll"
40
41diff --git a/data/libgcc_s_dw2-1.dll b/data/libgcc_s_dw2-1.dll
42new file mode 100644
43index 0000000..3ac46a5
44Binary files /dev/null and b/data/libgcc_s_dw2-1.dll differ
45diff --git a/data/libstdc++-6.dll b/data/libstdc++-6.dll
46index c4804b8..b0fd5e4 100644
47Binary files a/data/libstdc++-6.dll and b/data/libstdc++-6.dll differ
48diff --git a/data/libwinpthread-1.dll b/data/libwinpthread-1.dll
49index ac09bcc..511c25c 100644
50Binary files a/data/libwinpthread-1.dll and b/data/libwinpthread-1.dll differ
51diff --git a/data/makezip.sh.in b/data/makezip.sh.in
52index 96bb1cd..46f4859 100644
53--- a/data/makezip.sh.in
54+++ b/data/makezip.sh.in
55@@ -54,7 +54,7 @@ copy_libtool "src/putty" "fzputtygen.exe"
56 copy_libtool "src/fzshellext" "libfzshellext-0.dll" "fzshellext.dll"
57
58 cp "$top_srcdir/src/fzshellext/fzshellext_64.dll" "$targetdir/fzshellext_64.dll" || exit 1
59-cp "$top_srcdir/data/libgcc_s_sjlj-1.dll" "$targetdir/libgcc_s_sjlj-1.dll" || exit 1
60+cp "$top_srcdir/data/libgcc_s_dw2-1.dll" "$targetdir/libgcc_s_dw2-1.dll" || exit 1
61 cp "$top_srcdir/data/libstdc++-6.dll" "$targetdir/libstdc++-6.dll" || exit 1
62 cp "$top_srcdir/data/libwinpthread-1.dll" "$targetdir/libwinpthread-1.dll" || exit 1
63
64diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am
65index a13bf4b..61208a8 100644
66--- a/src/engine/Makefile.am
67+++ b/src/engine/Makefile.am
68@@ -20,6 +20,8 @@ libengine_a_SOURCES = \
69 FileZillaEngine.cpp \
70 file.cpp \
71 ftpcontrolsocket.cpp \
72+ gssinterface.cpp \
73+ gsssocket.cpp \
74 httpcontrolsocket.cpp \
75 iothread.cpp \
76 local_filesys.cpp \
77@@ -52,6 +54,7 @@ noinst_HEADERS = backend.h \
78 filezilla.h \
79 file.h \
80 ftpcontrolsocket.h \
81+ gsssocket.h \
82 httpcontrolsocket.h iothread.h \
83 logging_private.h \
84 pathcache.h \
85diff --git a/src/engine/ftpcontrolsocket.cpp b/src/engine/ftpcontrolsocket.cpp
86index 5d2c0ff..a92be07 100644
87--- a/src/engine/ftpcontrolsocket.cpp
88+++ b/src/engine/ftpcontrolsocket.cpp
89@@ -13,6 +13,7 @@
90 #include "transfersocket.h"
91 #include "local_filesys.h"
92 #include "proxy.h"
93+#include "gssinterface.h"
94
95 #include <wx/filename.h>
96 #include <wx/log.h>
97@@ -22,19 +23,21 @@
98 #include <algorithm>
99
100 #define LOGON_WELCOME 0
101-#define LOGON_AUTH_TLS 1
102-#define LOGON_AUTH_SSL 2
103-#define LOGON_AUTH_WAIT 3
104-#define LOGON_LOGON 4
105-#define LOGON_SYST 5
106-#define LOGON_FEAT 6
107-#define LOGON_CLNT 7
108-#define LOGON_OPTSUTF8 8
109-#define LOGON_PBSZ 9
110-#define LOGON_PROT 10
111-#define LOGON_OPTSMLST 11
112-#define LOGON_CUSTOMCOMMANDS 12
113-#define LOGON_DONE 13
114+#define LOGON_AUTH_GSS 1
115+#define LOGON_AUTH_ADAT 2
116+#define LOGON_AUTH_TLS 3
117+#define LOGON_AUTH_SSL 4
118+#define LOGON_AUTH_WAIT 5
119+#define LOGON_LOGON 6
120+#define LOGON_SYST 7
121+#define LOGON_FEAT 8
122+#define LOGON_CLNT 9
123+#define LOGON_OPTSUTF8 10
124+#define LOGON_PBSZ 11
125+#define LOGON_PROT 12
126+#define LOGON_OPTSMLST 13
127+#define LOGON_CUSTOMCOMMANDS 14
128+#define LOGON_DONE 15
129
130 CRawTransferOpData::CRawTransferOpData()
131 : COpData(Command::rawtransfer)
132@@ -118,22 +121,31 @@ struct t_loginCommand
133 class CFtpLogonOpData : public CConnectOpData
134 {
135 public:
136- CFtpLogonOpData()
137+ CFtpLogonOpData(CControlSocket *pSocket)
138 {
139 waitChallenge = false;
140 gotPassword = false;
141 waitForAsyncRequest = false;
142 gotFirstWelcomeLine = false;
143 ftp_proxy_type = 0;
144+ pControlSocket = pSocket;
145
146 customCommandIndex = 0;
147
148 for (int i = 0; i < LOGON_DONE; ++i)
149 neededCommands[i] = 1;
150+
151+ if (!IsGssLoaded())
152+ neededCommands[LOGON_AUTH_GSS] = 0;
153+
154+ neededCommands[LOGON_AUTH_ADAT] = 0;
155 }
156
157 virtual ~CFtpLogonOpData()
158+
159 {
160+ if (target_name)
161+ GssReleaseName(pControlSocket, (void **) &target_name);
162 }
163
164 wxString challenge; // Used for interactive logons
165@@ -149,6 +161,11 @@ public:
166 std::list<t_loginCommand> loginSequence;
167
168 int ftp_proxy_type;
169+
170+ CControlSocket *pControlSocket;
171+ wxString adatSend;
172+ void *target_name = NULL;
173+ int lastcode = -1;
174 };
175
176 class CFtpDeleteOpData : public COpData
177@@ -189,7 +206,9 @@ CFtpControlSocket::CFtpControlSocket(CFileZillaEnginePrivate & engine)
178 m_pendingReplies = 1;
179 m_pTlsSocket = 0;
180 m_protectDataChannel = false;
181+ m_protectCommandChannel = false;
182 m_lastTypeBinary = -1;
183+ m_protBufferSize = 0;
184
185 // Enable TCP_NODELAY, speeds things up a bit.
186 // Enable SO_KEEPALIVE, lots of clueless users have broken routers and
187@@ -406,6 +425,11 @@ void CFtpControlSocket::ParseResponse()
188 return;
189 }
190
191+ if (m_Response[0] == '6' && m_protectCommandChannel) {
192+ DecryptResponse();
193+ return;
194+ }
195+
196 if (m_Response[0] != '1') {
197 if (m_pendingReplies > 0)
198 m_pendingReplies--;
199@@ -719,6 +743,58 @@ int CFtpControlSocket::LogonParseResponse()
200 return FZ_REPLY_DISCONNECTED;
201 }
202 }
203+ else if (pData->opState == LOGON_AUTH_GSS)
204+ {
205+ if (code == 3) {
206+ pData->neededCommands[LOGON_AUTH_ADAT] = 1;
207+ pData->neededCommands[LOGON_AUTH_TLS] = 0;
208+ pData->neededCommands[LOGON_AUTH_SSL] = 0;
209+ pData->neededCommands[LOGON_AUTH_WAIT] = 0;
210+ pData->neededCommands[LOGON_PBSZ] = 1;
211+ pData->neededCommands[LOGON_PROT] = 1;
212+ m_protBufferSize =
213+ engine_.GetOptions().GetOptionVal(OPTION_GSS_PROTBUFSIZE);
214+ }
215+ }
216+ else if (pData->opState == LOGON_AUTH_ADAT)
217+ {
218+ if (code == 2 || code == 3) {
219+ /*
220+ * Scan this response to see if we got an ADAT
221+ * message. If we did, decode the data and
222+ * save it.
223+ */
224+
225+ int pos = m_Response.Find(_T("ADAT="));
226+
227+ pData->lastcode = code;
228+
229+ if (pos != wxNOT_FOUND) {
230+ wxString data = m_Response.Mid(pos + 5);
231+ bool more;
232+
233+ if (!ProcessAdatResponse(data, &more)) {
234+ LogMessage(MessageType::Status,
235+ _("Processing of ADAT response failed"));
236+ DoClose(FZ_REPLY_INTERNALERROR);
237+ return FZ_REPLY_ERROR;
238+ }
239+ /*
240+ * If we have a token to send, bump our
241+ * opState back so we can send it.
242+ */
243+
244+ if (more)
245+ pData->opState--;
246+
247+ }
248+ } else {
249+ /* Any other response is an error */
250+ LogMessage(MessageType::Status, _("Invalid response to ADAT"));
251+ DoClose(FZ_REPLY_INTERNALERROR);
252+ return FZ_REPLY_ERROR;
253+ }
254+ }
255 else if (pData->opState == LOGON_AUTH_TLS ||
256 pData->opState == LOGON_AUTH_SSL)
257 {
258@@ -893,10 +969,39 @@ int CFtpControlSocket::LogonParseResponse()
259 m_useUTF8 = false;
260 }
261 }
262- else if (pData->opState == LOGON_PROT) {
263- if (code == 2 || code == 3)
264+ else if (pData->opState == LOGON_PROT)
265+ {
266+ if (m_protectDataChannel && code != 2)
267+ m_protectDataChannel = false;
268+ else if (code == 2 || code == 3)
269 m_protectDataChannel = true;
270 }
271+ else if (pData->opState == LOGON_PBSZ)
272+ {
273+ if (code != 2)
274+ m_protectDataChannel = false;
275+
276+ if (m_protectDataChannel) {
277+ /*
278+ * Parse the PBSZ response to see if the buffer size
279+ * has been specified.
280+ */
281+
282+ int pos = m_Response.Find(_T("PBSZ="));
283+
284+ if (pos != wxNOT_FOUND) {
285+ wxString data = m_Response.Mid(pos + 5);
286+ unsigned long retbufsz;
287+
288+ if (! data.ToCULong(&retbufsz)) {
289+ LogMessage(MessageType::Status, _("Invalid PBSZ response, ignoring."));
290+ } else {
291+ if (retbufsz < m_protBufferSize)
292+ m_protBufferSize = retbufsz;
293+ }
294+ }
295+ }
296+ }
297 else if (pData->opState == LOGON_CUSTOMCOMMANDS) {
298 ++pData->customCommandIndex;
299 if (pData->customCommandIndex < m_pCurrentServer->GetPostLoginCommands().size())
300@@ -1053,6 +1158,12 @@ int CFtpControlSocket::LogonSend()
301 res = FZ_REPLY_WOULDBLOCK;
302 LogMessage(MessageType::Debug_Info, _T("LogonSend() called during LOGON_AUTH_WAIT, ignoring"));
303 break;
304+ case LOGON_AUTH_GSS:
305+ res = SendCommand(_T("AUTH GSSAPI"), false, false);
306+ break;
307+ case LOGON_AUTH_ADAT:
308+ res = SendGssAdat();
309+ break;
310 case LOGON_AUTH_TLS:
311 res = SendCommand(_T("AUTH TLS"), false, false);
312 break;
313@@ -1139,10 +1250,20 @@ int CFtpControlSocket::LogonSend()
314 res = SendCommand(_T("OPTS UTF8 ON"));
315 break;
316 case LOGON_PBSZ:
317- res = SendCommand(_T("PBSZ 0"));
318+ if (IsGssLoaded() &&
319+ engine_.GetOptions().GetOptionVal(OPTION_GSS_ENCRYPTDATACHAN)) {
320+ m_protectDataChannel = true;
321+ res = SendCommand(_T("PBSZ ") +
322+ std::to_string(m_protBufferSize));
323+ }
324+ else
325+ res = SendCommand(_T("PBSZ 0"));
326 break;
327 case LOGON_PROT:
328- res = SendCommand(_T("PROT P"));
329+ if (m_protectDataChannel)
330+ res = SendCommand(_T("PROT P"));
331+ else
332+ res = SendCommand(_T("PROT C"));
333 break;
334 case LOGON_CUSTOMCOMMANDS:
335 if (pData->customCommandIndex >= m_pCurrentServer->GetPostLoginCommands().size())
336@@ -1183,9 +1304,28 @@ int CFtpControlSocket::GetReplyCode() const
337 }
338 }
339
340+int CFtpControlSocket::GetFullReplyCode() const
341+{
342+ unsigned long ret;
343+
344+ if (m_Response.empty()) {
345+ return 0;
346+ }
347+
348+ wxString substr = m_Response.Mid(0, 3);
349+
350+ if (! substr.ToULong(&ret)) {
351+ return 0;
352+ }
353+
354+ return ret;
355+}
356+
357 bool CFtpControlSocket::SendCommand(wxString const& str, bool maskArgs, bool measureRTT)
358 {
359 int pos;
360+ wxCharBuffer buffer;
361+
362 if (maskArgs && (pos = str.Find(_T(" "))) != -1)
363 {
364 wxString stars('*', str.Length() - pos - 1);
365@@ -1194,12 +1334,23 @@ bool CFtpControlSocket::SendCommand(wxString const& str, bool maskArgs, bool mea
366 else
367 LogMessageRaw(MessageType::Command, str);
368
369- wxCharBuffer buffer = ConvToServer(str + _T("\r\n"));
370- if (!buffer)
371+ if (m_protectCommandChannel)
372 {
373- LogMessage(MessageType::Error, _T("Failed to convert command to 8 bit charset"));
374- return false;
375+ buffer = ProtectCommand(str);
376+ if (!buffer)
377+ {
378+ LogMessage(MessageType::Error, _T("Failed to encrypt command"));
379+ return false;
380+ }
381+ } else {
382+ buffer = ConvToServer(str + _T("\r\n"));
383+ if (!buffer)
384+ {
385+ LogMessage(MessageType::Error, _T("Failed to convert command to 8 bit charset"));
386+ return false;
387+ }
388 }
389+
390 unsigned int len = (unsigned int)strlen(buffer);
391 bool res = CRealControlSocket::Send(buffer, len);
392 if (res)
393@@ -4275,7 +4426,7 @@ int CFtpControlSocket::Connect(const CServer &server)
394 delete m_pCurOpData;
395 }
396
397- CFtpLogonOpData* pData = new CFtpLogonOpData;
398+ CFtpLogonOpData* pData = new CFtpLogonOpData(this);
399 m_pCurOpData = pData;
400
401 // Do not use FTP proxy if generic proxy is set
402@@ -4472,6 +4623,176 @@ int CFtpControlSocket::ParseSubcommandResult(int prevResult)
403 return FZ_REPLY_ERROR;
404 }
405
406+int CFtpControlSocket::SendGssAdat()
407+{
408+ CFtpLogonOpData *pData = reinterpret_cast<CFtpLogonOpData *>(m_pCurOpData);
409+ wxString output_token;
410+ bool complete;
411+
412+ //
413+ // If data is left over from a init_sec_context call send it now
414+ //
415+
416+ if (pData->adatSend.length() > 0) {
417+ int ret = SendCommand("ADAT " + pData->adatSend, false, false);
418+ pData->adatSend.Clear();
419+ return ret;
420+ }
421+
422+ //
423+ // The code here should only be invoked the first time around
424+ //
425+
426+ if (! pData->target_name) {
427+ wxString target = "host@" + pData->host;
428+ if (!GssImportName(this, target, &pData->target_name))
429+ return false;
430+ }
431+
432+ if (!GssInitSecContext(this, &gcontext, pData->target_name, true,
433+ wxEmptyString, &output_token, &complete))
434+ return false;
435+
436+ if (output_token.Length() > 0) {
437+ if (!SendCommand(_T("ADAT ") + output_token, false, false))
438+ return false;
439+ }
440+
441+ /*
442+ * If lastcode is set, it should be either 2 (complete) or 3
443+ * (more exchanges needed). If it's 2, we should be done
444+ * (GSS_S_COMPLETE).
445+ */
446+
447+ if (pData->lastcode == 2) {
448+ if (complete) {
449+ LogMessage(MessageType::Status, _("GSSAPI Authentication successful"));
450+ m_protectCommandChannel = true;
451+ } else {
452+ return false;
453+ }
454+ }
455+
456+ return true;
457+}
458+
459+bool CFtpControlSocket::ProcessAdatResponse(wxString recvdata,
460+ bool *more)
461+{
462+ CFtpLogonOpData *pData = reinterpret_cast<CFtpLogonOpData *>(m_pCurOpData);
463+ bool complete;
464+
465+ if (!GssInitSecContext(this, &gcontext, pData->target_name, true,
466+ recvdata, &pData->adatSend, &complete)) {
467+ return false;
468+ }
469+
470+ if (pData->adatSend.Length() > 0) {
471+ *more = true;
472+ } else {
473+ *more = false;
474+ }
475+
476+ /*
477+ * If we're done, mark it.
478+ */
479+
480+ if (pData->lastcode == 2 && complete) {
481+ LogMessage(MessageType::Status, _("GSSAPI Authentication successful"));
482+ m_protectCommandChannel = true;
483+ }
484+
485+ return true;
486+}
487+
488+wxCharBuffer CFtpControlSocket::ProtectCommand(wxString const& str)
489+{
490+ wxCharBuffer input = ConvToServer(str);
491+ wxString output;
492+
493+ if (!GssWrap(this, gcontext, true, input, &output))
494+ {
495+ return wxCharBuffer();
496+ }
497+
498+ wxString retstr = _("ENC ") + output + _("\r\n");
499+
500+ return retstr.c_str();
501+}
502+
503+void CFtpControlSocket::DecryptResponse()
504+{
505+ int code = GetFullReplyCode();
506+ wxString enc_response;
507+ bool encrypted;
508+ wxMemoryBuffer output;
509+
510+ if (code != 631 && code != 632 && code != 633)
511+ {
512+ LogMessage(MessageType::Error, _T("Unknown response code %d"), code);
513+ return;
514+ }
515+
516+ bool protect = (code != 631);
517+
518+ std::list<wxString>::iterator iter = m_MultilineResponseLines.begin();
519+
520+ while (iter != m_MultilineResponseLines.end())
521+ {
522+ enc_response.Append(iter->Mid(4));
523+ iter++;
524+ }
525+
526+ enc_response.Append(m_Response.Mid(4));
527+
528+ if (!GssUnwrap(this, gcontext, enc_response, &output, &encrypted))
529+ return;
530+
531+ if (protect != encrypted)
532+ {
533+ LogMessage(MessageType::Error, _("Response code did not match encryption level!"));
534+ return;
535+ }
536+ /*
537+ * Because of the way multiline reponses are dealt with in
538+ * GSSAPI, we need to replicate part of OnReceive() here (but
539+ * it's been adapted).
540+ */
541+
542+ char *start, *bufstart;
543+ start = bufstart = (char *) malloc(output.GetDataLen() + 1);
544+ memcpy(bufstart, output.GetData(), output.GetDataLen());
545+ bufstart[output.GetDataLen()] = '\0';
546+
547+ for (int i = start - bufstart; i < output.GetDataLen() + 1; ++i)
548+ {
549+ char& p = bufstart[i];
550+ if (p == '\r' ||
551+ p == '\n' ||
552+ p == 0)
553+ {
554+ int len = i - (start - bufstart);
555+ if (!len)
556+ {
557+ ++start;
558+ continue;
559+ }
560+
561+ p = 0;
562+ wxString line = ConvToLocal(start, i + 1 - (start - bufstart));
563+ start = bufstart + i + 1;
564+
565+ ParseLine(line);
566+
567+ // Abort if connection got closed
568+ if (!m_pCurrentServer)
569+ break;
570+ }
571+ }
572+
573+ free(bufstart);
574+}
575+
576 void CFtpControlSocket::operator()(CEventBase const& ev)
577 {
578 if (Dispatch<CTimerEvent>(ev, this, &CFtpControlSocket::OnTimer)) {
579diff --git a/src/engine/ftpcontrolsocket.h b/src/engine/ftpcontrolsocket.h
580index a1db93c..de7a2d0 100644
581--- a/src/engine/ftpcontrolsocket.h
582+++ b/src/engine/ftpcontrolsocket.h
583@@ -96,11 +96,18 @@ protected:
584 virtual int ParseSubcommandResult(int prevResult);
585
586 int GetReplyCode() const;
587+ int GetFullReplyCode() const;
588
589 int Logon();
590 int LogonParseResponse();
591 int LogonSend();
592
593+ int SendGssAdat();
594+ bool ProcessAdatResponse(wxString recvdata, bool *more);
595+ void ReportGssError(uint32_t major, uint32_t minor);
596+ wxCharBuffer ProtectCommand(wxString const& str);
597+ void DecryptResponse();
598+
599 wxString GetPassiveCommand(CRawTransferOpData& data);
600 bool ParsePasvResponse(CRawTransferOpData* pData);
601 bool ParseEpsvResponse(CRawTransferOpData* pData);
602@@ -139,6 +146,13 @@ protected:
603
604 CTlsSocket* m_pTlsSocket;
605 bool m_protectDataChannel;
606+ bool m_protectCommandChannel;
607+
608+ // Context structure for GSSAPI
609+ void *gcontext = NULL;
610+
611+ // Protection buffer size
612+ int m_protBufferSize;
613
614 int m_lastTypeBinary;
615
616diff --git a/src/engine/gssinterface.cpp b/src/engine/gssinterface.cpp
617new file mode 100644
618index 0000000..1af0fb2
619--- /dev/null
620+++ b/src/engine/gssinterface.cpp
621@@ -0,0 +1,982 @@
622+#include <filezilla.h>
623+#include "ControlSocket.h"
624+#include "../interface/Options.h"
625+
626+#include <wx/dynlib.h>
627+#include <wx/base64.h>
628+#include <wx/filename.h>
629+
630+#include "gssinterface.h"
631+
632+/*
633+ * We include the generic header file here from the GSSAPI RFC, so we
634+ * don't need to depend on it (but only some of it).
635+ */
636+
637+/* -*- mode: c; indent-tabs-mode: nil -*- */
638+/*
639+ * Copyright 1993 by OpenVision Technologies, Inc.
640+ *
641+ * Permission to use, copy, modify, distribute, and sell this software
642+ * and its documentation for any purpose is hereby granted without fee,
643+ * provided that the above copyright notice appears in all copies and
644+ * that both that copyright notice and this permission notice appear in
645+ * supporting documentation, and that the name of OpenVision not be used
646+ * in advertising or publicity pertaining to distribution of the software
647+ * without specific, written prior permission. OpenVision makes no
648+ * representations about the suitability of this software for any
649+ * purpose. It is provided "as is" without express or implied warranty.
650+ *
651+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
652+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
653+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
654+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
655+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
656+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
657+ * PERFORMANCE OF THIS SOFTWARE.
658+ */
659+
660+/*
661+ * Determine platform-dependent configuration.
662+ */
663+
664+#if defined(__MACH__) && defined(__APPLE__)
665+# include <TargetConditionals.h>
666+# if TARGET_RT_MAC_CFM
667+# error "Use KfM 4.0 SDK headers for CFM compilation."
668+# endif
669+#endif
670+
671+#ifndef __has_extension
672+#define __has_extension(x) 0
673+#endif
674+
675+#ifndef GSSKRB_APPLE_DEPRECATED
676+#if __has_extension(attribute_deprecated_with_message)
677+#define GSSKRB_APPLE_DEPRECATED(x) __attribute__((deprecated(x)))
678+#else
679+#if !defined(__GNUC__) && !defined(__attribute__)
680+#define __attribute__(x)
681+#endif
682+#define GSSKRB_APPLE_DEPRECATED(x) __attribute__((deprecated))
683+#endif
684+#endif
685+
686+#ifdef __cplusplus
687+extern "C" {
688+#endif /* __cplusplus */
689+
690+#if TARGET_OS_MAC
691+# pragma pack(push,2)
692+#endif
693+
694+#if 0
695+#if defined(_MSDOS) || defined(_WIN32)
696+#include <win-mac.h>
697+#endif
698+#endif
699+
700+#ifndef KRB5_CALLCONV
701+#define KRB5_CALLCONV
702+#define KRB5_CALLCONV_C
703+#endif
704+
705+/*
706+ * First, include stddef.h to get size_t defined.
707+ */
708+#include <stddef.h>
709+
710+/*
711+ * POSIX says that sys/types.h is where size_t is defined.
712+ */
713+#include <sys/types.h>
714+
715+/*
716+ * $Id: gssapi.hin 20876 2008-10-15 21:58:43Z tlyu $
717+ */
718+
719+/*
720+ * First, define the three platform-dependent pointer types.
721+ */
722+
723+struct gss_name_struct;
724+typedef struct gss_name_struct * gss_name_t;
725+
726+struct gss_cred_id_struct;
727+typedef struct gss_cred_id_struct * gss_cred_id_t;
728+
729+struct gss_ctx_id_struct;
730+typedef struct gss_ctx_id_struct * gss_ctx_id_t;
731+
732+/*
733+ * The following type must be defined as the smallest natural unsigned integer
734+ * supported by the platform that has at least 32 bits of precision.
735+ */
736+typedef uint32_t gss_uint32;
737+typedef int32_t gss_int32;
738+
739+#ifdef OM_STRING
740+/*
741+ * We have included the xom.h header file. Use the definition for
742+ * OM_object identifier.
743+ */
744+typedef OM_object_identifier gss_OID_desc, *gss_OID;
745+#else /* OM_STRING */
746+/*
747+ * We can't use X/Open definitions, so roll our own.
748+ */
749+typedef gss_uint32 OM_uint32;
750+
751+typedef struct gss_OID_desc_struct {
752+ OM_uint32 length;
753+ void *elements;
754+} gss_OID_desc, *gss_OID;
755+#endif /* OM_STRING */
756+
757+typedef struct gss_OID_set_desc_struct {
758+ size_t count;
759+ gss_OID elements;
760+} gss_OID_set_desc, *gss_OID_set;
761+
762+typedef struct gss_buffer_desc_struct {
763+ size_t length;
764+ void *value;
765+} gss_buffer_desc, *gss_buffer_t;
766+
767+typedef struct gss_channel_bindings_struct {
768+ OM_uint32 initiator_addrtype;
769+ gss_buffer_desc initiator_address;
770+ OM_uint32 acceptor_addrtype;
771+ gss_buffer_desc acceptor_address;
772+ gss_buffer_desc application_data;
773+} *gss_channel_bindings_t;
774+
775+/*
776+ * For now, define a QOP-type as an OM_uint32 (pending resolution of ongoing
777+ * discussions).
778+ */
779+typedef OM_uint32 gss_qop_t;
780+typedef int gss_cred_usage_t;
781+
782+/*
783+ * Flag bits for context-level services.
784+ */
785+#define GSS_C_DELEG_FLAG 1
786+#define GSS_C_MUTUAL_FLAG 2
787+#define GSS_C_REPLAY_FLAG 4
788+#define GSS_C_SEQUENCE_FLAG 8
789+#define GSS_C_CONF_FLAG 16
790+#define GSS_C_INTEG_FLAG 32
791+#define GSS_C_ANON_FLAG 64
792+#define GSS_C_PROT_READY_FLAG 128
793+#define GSS_C_TRANS_FLAG 256
794+#define GSS_C_DELEG_POLICY_FLAG 32768
795+#define GSS_C_NO_UI_FLAG 0x80000000
796+
797+/*
798+ * Credential usage options
799+ */
800+#define GSS_C_BOTH 0
801+#define GSS_C_INITIATE 1
802+#define GSS_C_ACCEPT 2
803+
804+#define GSS_C_OPTION_MASK 0xffff
805+#define GSS_C_CRED_NO_UI 0x10000
806+
807+
808+/*
809+ * Status code types for gss_display_status
810+ */
811+#define GSS_C_GSS_CODE 1
812+#define GSS_C_MECH_CODE 2
813+
814+/*
815+ * Various Null values.
816+ */
817+#define GSS_C_NO_NAME ((gss_name_t) 0)
818+#define GSS_C_NO_BUFFER ((gss_buffer_t) 0)
819+#define GSS_C_NO_OID ((gss_OID) 0)
820+#define GSS_C_NO_OID_SET ((gss_OID_set) 0)
821+#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0)
822+#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)
823+#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)
824+#define GSS_C_EMPTY_BUFFER {0, NULL}
825+
826+/*
827+ * Some alternate names for a couple of the above values. These are defined
828+ * for V1 compatibility.
829+ */
830+#define GSS_C_NULL_OID GSS_C_NO_OID
831+#define GSS_C_NULL_OID_SET GSS_C_NO_OID_SET
832+
833+/*
834+ * Define the default Quality of Protection for per-message services. Note
835+ * that an implementation that offers multiple levels of QOP may either reserve
836+ * a value (for example zero, as assumed here) to mean "default protection", or
837+ * alternatively may simply equate GSS_C_QOP_DEFAULT to a specific explicit
838+ * QOP value. However a value of 0 should always be interpreted by a GSSAPI
839+ * implementation as a request for the default protection level.
840+ */
841+#define GSS_C_QOP_DEFAULT 0
842+
843+/*
844+ * Expiration time of 2^32-1 seconds means infinite lifetime for a
845+ * credential or security context
846+ */
847+#define GSS_C_INDEFINITE ((OM_uint32) 0xfffffffful)
848+
849+
850+/* Major status codes */
851+
852+#define GSS_S_COMPLETE 0
853+
854+/*
855+ * Some "helper" definitions to make the status code macros obvious.
856+ */
857+#define GSS_C_CALLING_ERROR_OFFSET 24
858+#define GSS_C_ROUTINE_ERROR_OFFSET 16
859+#define GSS_C_SUPPLEMENTARY_OFFSET 0
860+#define GSS_C_CALLING_ERROR_MASK ((OM_uint32) 0377ul)
861+#define GSS_C_ROUTINE_ERROR_MASK ((OM_uint32) 0377ul)
862+#define GSS_C_SUPPLEMENTARY_MASK ((OM_uint32) 0177777ul)
863+
864+/*
865+ * The macros that test status codes for error conditions. Note that the
866+ * GSS_ERROR() macro has changed slightly from the V1 GSSAPI so that it now
867+ * evaluates its argument only once.
868+ */
869+#define GSS_CALLING_ERROR(x) \
870+ ((x) & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
871+#define GSS_ROUTINE_ERROR(x) \
872+ ((x) & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
873+#define GSS_SUPPLEMENTARY_INFO(x) \
874+ ((x) & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
875+#define GSS_ERROR(x) \
876+ ((x) & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \
877+ (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
878+
879+/*
880+ * Now the actual status code definitions
881+ */
882+
883+/*
884+ * Calling errors:
885+ */
886+#define GSS_S_CALL_INACCESSIBLE_READ \
887+ (((OM_uint32) 1ul) << GSS_C_CALLING_ERROR_OFFSET)
888+#define GSS_S_CALL_INACCESSIBLE_WRITE \
889+ (((OM_uint32) 2ul) << GSS_C_CALLING_ERROR_OFFSET)
890+#define GSS_S_CALL_BAD_STRUCTURE \
891+ (((OM_uint32) 3ul) << GSS_C_CALLING_ERROR_OFFSET)
892+
893+/*
894+ * Routine errors:
895+ */
896+#define GSS_S_BAD_MECH (((OM_uint32) 1ul) << GSS_C_ROUTINE_ERROR_OFFSET)
897+#define GSS_S_BAD_NAME (((OM_uint32) 2ul) << GSS_C_ROUTINE_ERROR_OFFSET)
898+#define GSS_S_BAD_NAMETYPE (((OM_uint32) 3ul) << GSS_C_ROUTINE_ERROR_OFFSET)
899+#define GSS_S_BAD_BINDINGS (((OM_uint32) 4ul) << GSS_C_ROUTINE_ERROR_OFFSET)
900+#define GSS_S_BAD_STATUS (((OM_uint32) 5ul) << GSS_C_ROUTINE_ERROR_OFFSET)
901+#define GSS_S_BAD_SIG (((OM_uint32) 6ul) << GSS_C_ROUTINE_ERROR_OFFSET)
902+#define GSS_S_NO_CRED (((OM_uint32) 7ul) << GSS_C_ROUTINE_ERROR_OFFSET)
903+#define GSS_S_NO_CONTEXT (((OM_uint32) 8ul) << GSS_C_ROUTINE_ERROR_OFFSET)
904+#define GSS_S_DEFECTIVE_TOKEN (((OM_uint32) 9ul) << GSS_C_ROUTINE_ERROR_OFFSET)
905+#define GSS_S_DEFECTIVE_CREDENTIAL \
906+ (((OM_uint32) 10ul) << GSS_C_ROUTINE_ERROR_OFFSET)
907+#define GSS_S_CREDENTIALS_EXPIRED \
908+ (((OM_uint32) 11ul) << GSS_C_ROUTINE_ERROR_OFFSET)
909+#define GSS_S_CONTEXT_EXPIRED \
910+ (((OM_uint32) 12ul) << GSS_C_ROUTINE_ERROR_OFFSET)
911+#define GSS_S_FAILURE (((OM_uint32) 13ul) << GSS_C_ROUTINE_ERROR_OFFSET)
912+#define GSS_S_BAD_QOP (((OM_uint32) 14ul) << GSS_C_ROUTINE_ERROR_OFFSET)
913+#define GSS_S_UNAUTHORIZED (((OM_uint32) 15ul) << GSS_C_ROUTINE_ERROR_OFFSET)
914+#define GSS_S_UNAVAILABLE (((OM_uint32) 16ul) << GSS_C_ROUTINE_ERROR_OFFSET)
915+#define GSS_S_DUPLICATE_ELEMENT \
916+ (((OM_uint32) 17ul) << GSS_C_ROUTINE_ERROR_OFFSET)
917+#define GSS_S_NAME_NOT_MN \
918+ (((OM_uint32) 18ul) << GSS_C_ROUTINE_ERROR_OFFSET)
919+
920+/*
921+ * Supplementary info bits:
922+ */
923+#define GSS_S_CONTINUE_NEEDED (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
924+#define GSS_S_DUPLICATE_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
925+#define GSS_S_OLD_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
926+#define GSS_S_UNSEQ_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
927+#define GSS_S_GAP_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
928+
929+
930+/*
931+ * Finally, function prototypes for the GSSAPI routines.
932+ */
933+
934+#if defined (_WIN32) && defined (_MSC_VER)
935+# ifdef GSS_DLL_FILE
936+# define GSS_DLLIMP __declspec(dllexport)
937+# else
938+# define GSS_DLLIMP __declspec(dllimport)
939+# endif
940+#else
941+# define GSS_DLLIMP
942+#endif
943+
944+/* Reserved static storage for GSS_oids. Comments are quotes from RFC 2744.
945+ *
946+ * The implementation must reserve static storage for a
947+ * gss_OID_desc object containing the value
948+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"},
949+ * corresponding to an object-identifier value of
950+ * {iso(1) member-body(2) United States(840) mit(113554)
951+ * infosys(1) gssapi(2) generic(1) user_name(1)}. The constant
952+ * GSS_C_NT_USER_NAME should be initialized to point
953+ * to that gss_OID_desc.
954+ */
955+GSS_DLLIMP extern gss_OID GSS_C_NT_USER_NAME;
956+
957+/*
958+ * The implementation must reserve static storage for a
959+ * gss_OID_desc object containing the value
960+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"},
961+ * corresponding to an object-identifier value of
962+ * {iso(1) member-body(2) United States(840) mit(113554)
963+ * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
964+ * The constant GSS_C_NT_MACHINE_UID_NAME should be
965+ * initialized to point to that gss_OID_desc.
966+ */
967+GSS_DLLIMP extern gss_OID GSS_C_NT_MACHINE_UID_NAME;
968+
969+/*
970+ * The implementation must reserve static storage for a
971+ * gss_OID_desc object containing the value
972+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"},
973+ * corresponding to an object-identifier value of
974+ * {iso(1) member-body(2) United States(840) mit(113554)
975+ * infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
976+ * The constant GSS_C_NT_STRING_UID_NAME should be
977+ * initialized to point to that gss_OID_desc.
978+ */
979+GSS_DLLIMP extern gss_OID GSS_C_NT_STRING_UID_NAME;
980+
981+/*
982+ * The implementation must reserve static storage for a
983+ * gss_OID_desc object containing the value
984+ * {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
985+ * corresponding to an object-identifier value of
986+ * {iso(1) org(3) dod(6) internet(1) security(5)
987+ * nametypes(6) gss-host-based-services(2)). The constant
988+ * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
989+ * to that gss_OID_desc. This is a deprecated OID value, and
990+ * implementations wishing to support hostbased-service names
991+ * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
992+ * defined below, to identify such names;
993+ * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
994+ * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
995+ * parameter, but should not be emitted by GSS-API
996+ * implementations
997+ */
998+GSS_DLLIMP extern gss_OID GSS_C_NT_HOSTBASED_SERVICE_X;
999+
1000+/*
1001+ * The implementation must reserve static storage for a
1002+ * gss_OID_desc object containing the value
1003+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
1004+ * "\x01\x02\x01\x04"}, corresponding to an
1005+ * object-identifier value of {iso(1) member-body(2)
1006+ * Unites States(840) mit(113554) infosys(1) gssapi(2)
1007+ * generic(1) service_name(4)}. The constant
1008+ * GSS_C_NT_HOSTBASED_SERVICE should be initialized
1009+ * to point to that gss_OID_desc.
1010+ */
1011+static gss_OID *p_GSS_C_NT_HOSTBASED_SERVICE;
1012+
1013+/*
1014+ * The implementation must reserve static storage for a
1015+ * gss_OID_desc object containing the value
1016+ * {6, (void *)"\x2b\x06\01\x05\x06\x03"},
1017+ * corresponding to an object identifier value of
1018+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
1019+ * 6(nametypes), 3(gss-anonymous-name)}. The constant
1020+ * and GSS_C_NT_ANONYMOUS should be initialized to point
1021+ * to that gss_OID_desc.
1022+ */
1023+GSS_DLLIMP extern gss_OID GSS_C_NT_ANONYMOUS;
1024+
1025+
1026+/*
1027+ * The implementation must reserve static storage for a
1028+ * gss_OID_desc object containing the value
1029+ * {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
1030+ * corresponding to an object-identifier value of
1031+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
1032+ * 6(nametypes), 4(gss-api-exported-name)}. The constant
1033+ * GSS_C_NT_EXPORT_NAME should be initialized to point
1034+ * to that gss_OID_desc.
1035+ */
1036+GSS_DLLIMP extern gss_OID GSS_C_NT_EXPORT_NAME;
1037+
1038+/* Function Prototypes */
1039+
1040+static OM_uint32 KRB5_CALLCONV
1041+(*p_gss_init_sec_context)(
1042+ OM_uint32 *, /* minor_status */
1043+ gss_cred_id_t, /* claimant_cred_handle */
1044+ gss_ctx_id_t *, /* context_handle */
1045+ gss_name_t, /* target_name */
1046+ gss_OID, /* mech_type (used to be const) */
1047+ OM_uint32, /* req_flags */
1048+ OM_uint32, /* time_req */
1049+ gss_channel_bindings_t, /* input_chan_bindings */
1050+ gss_buffer_t, /* input_token */
1051+ gss_OID *, /* actual_mech_type */
1052+ gss_buffer_t, /* output_token */
1053+ OM_uint32 *, /* ret_flags */
1054+ OM_uint32 *) /* time_rec */
1055+ = NULL;
1056+
1057+
1058+static OM_uint32 KRB5_CALLCONV
1059+(*p_gss_delete_sec_context)(
1060+ OM_uint32 *, /* minor_status */
1061+ gss_ctx_id_t *, /* context_handle */
1062+ gss_buffer_t); /* output_token */
1063+
1064+
1065+static OM_uint32 KRB5_CALLCONV
1066+(*p_gss_wrap)(
1067+ OM_uint32 *, /* minor_status */
1068+ gss_ctx_id_t, /* context_handle */
1069+ int, /* conf_req_flag */
1070+ gss_qop_t, /* qop_req */
1071+ gss_buffer_t, /* input_message_buffer */
1072+ int *, /* conf_state */
1073+ gss_buffer_t) /* output_message_buffer */
1074+ = NULL;
1075+
1076+static OM_uint32 KRB5_CALLCONV
1077+(*p_gss_unwrap)(
1078+ OM_uint32 *, /* minor_status */
1079+ gss_ctx_id_t, /* context_handle */
1080+ gss_buffer_t, /* input_message_buffer */
1081+ gss_buffer_t, /* output_message_buffer */
1082+ int *, /* conf_state */
1083+ gss_qop_t *) /* qop_state */
1084+ = NULL;
1085+
1086+
1087+static OM_uint32 KRB5_CALLCONV
1088+(*p_gss_display_status)(
1089+ OM_uint32 *, /* minor_status */
1090+ OM_uint32, /* status_value */
1091+ int, /* status_type */
1092+ gss_OID, /* mech_type (used to be const) */
1093+ OM_uint32 *, /* message_context */
1094+ gss_buffer_t) /* status_string */
1095+ = NULL;
1096+
1097+
1098+static OM_uint32 KRB5_CALLCONV
1099+(*p_gss_import_name)(
1100+ OM_uint32 *, /* minor_status */
1101+ gss_buffer_t, /* input_name_buffer */
1102+ gss_OID, /* input_name_type(used to be const) */
1103+ gss_name_t *) /* output_name */
1104+ = NULL;
1105+
1106+static OM_uint32 KRB5_CALLCONV
1107+(*p_gss_release_name)(
1108+ OM_uint32 *, /* minor_status */
1109+ gss_name_t *) /* input_name */
1110+ = NULL;
1111+
1112+static OM_uint32 KRB5_CALLCONV
1113+(*p_gss_release_buffer)(
1114+ OM_uint32 *, /* minor_status */
1115+ gss_buffer_t) /* buffer */
1116+ = NULL;
1117+
1118+static OM_uint32 KRB5_CALLCONV
1119+(*p_gss_wrap_size_limit)(
1120+ OM_uint32 *, /* minor_status */
1121+ gss_ctx_id_t, /* context_handle */
1122+ int, /* conf_req_flag */
1123+ gss_qop_t, /* qop_req */
1124+ OM_uint32, /* req_output_size */
1125+ OM_uint32 *) /* max_input_size */
1126+ = NULL;
1127+
1128+#if TARGET_OS_MAC
1129+# pragma pack(pop)
1130+#endif
1131+
1132+#ifdef __cplusplus
1133+}
1134+#endif
1135+
1136+/* XXXX these are not part of the GSSAPI C bindings! (but should be) */
1137+
1138+#define GSS_CALLING_ERROR_FIELD(x) \
1139+ (((x) >> GSS_C_CALLING_ERROR_OFFSET) & GSS_C_CALLING_ERROR_MASK)
1140+#define GSS_ROUTINE_ERROR_FIELD(x) \
1141+ (((x) >> GSS_C_ROUTINE_ERROR_OFFSET) & GSS_C_ROUTINE_ERROR_MASK)
1142+#define GSS_SUPPLEMENTARY_INFO_FIELD(x) \
1143+ (((x) >> GSS_C_SUPPLEMENTARY_OFFSET) & GSS_C_SUPPLEMENTARY_MASK)
1144+
1145+/* XXXX This is a necessary evil until the spec is fixed */
1146+#define GSS_S_CRED_UNAVAIL GSS_S_FAILURE
1147+
1148+static int tried_load = 0;
1149+
1150+static struct symbol_list
1151+{
1152+ const char *symname;
1153+ void **symptr;
1154+} gssapi_symlist[] =
1155+{
1156+ { "gss_init_sec_context", (void **) &p_gss_init_sec_context },
1157+ { "gss_delete_sec_context", (void **) &p_gss_delete_sec_context },
1158+ { "gss_release_buffer", (void **) &p_gss_release_buffer },
1159+ { "gss_release_name", (void **) &p_gss_release_name },
1160+ { "gss_display_status", (void **) &p_gss_display_status },
1161+ { "gss_import_name", (void **) &p_gss_import_name },
1162+ { "gss_wrap", (void **) &p_gss_wrap },
1163+ { "gss_unwrap", (void **) &p_gss_unwrap },
1164+ { "gss_wrap_size_limit", (void **) &p_gss_wrap_size_limit },
1165+ { "GSS_C_NT_HOSTBASED_SERVICE", (void **) &p_GSS_C_NT_HOSTBASED_SERVICE },
1166+ { NULL, NULL},
1167+};
1168+
1169+static wxDynamicLibrary gsslib;
1170+static wxString gsslibname =
1171+#ifdef DEFAULT_GSS_LIBRARY
1172+ DEFAULT_GSS_LIBRARY ;
1173+#elif __WXMAC__
1174+ "/usr/lib/libgssapi_krb5.dylib";
1175+#elif __WXMSW__
1176+ "C:\\Program Files\\MIT Kerberos\\GSSAPI32.DLL";
1177+#else
1178+ "/usr/lib/libgssapi_krb5.so";
1179+#endif
1180+
1181+static wxString gssloaderr;
1182+
1183+static void LogGssError(CControlSocket *psocket, OM_uint32 maj, OM_uint32 min);
1184+
1185+bool IsGssLoaded(void)
1186+{
1187+ if (! COptions::Get()->GetOptionVal(OPTION_GSS_ENABLE))
1188+ return false;
1189+
1190+ if (!gsslib.IsLoaded() && !tried_load) {
1191+ tried_load = 1;
1192+
1193+ wxString libname = COptions::Get()->GetOption(OPTION_GSS_LIBRARY);
1194+
1195+ return LoadGssLibrary(libname.IsEmpty() ? gsslibname : libname);
1196+ }
1197+
1198+ return gsslib.IsLoaded();
1199+}
1200+
1201+bool LoadGssLibrary(const wxString &libname)
1202+{
1203+ int i;
1204+
1205+ tried_load = 1;
1206+
1207+ if (gsslib.IsLoaded()) {
1208+ gsslib.Unload();
1209+
1210+ for (i = 0; gssapi_symlist[i].symname != NULL; i++)
1211+ *gssapi_symlist[i].symptr = NULL;
1212+ }
1213+
1214+ gsslibname = libname;
1215+
1216+#ifdef __WXMSW__
1217+ /*
1218+ * On Windows we need to set the DLL path so that the GSSAPI library
1219+ * can pull in any necessary symbols from other libraries (such as
1220+ * the Kerberos library). Set the path based on the directory of
1221+ * the GSSAPI library.
1222+ */
1223+
1224+ wxFileName dirname(libname);
1225+ wxString dir = dirname.GetPath();
1226+
1227+ SetDllDirectory(dir.t_str());
1228+#endif
1229+
1230+ if (gsslib.Load(gsslibname, wxDL_DEFAULT | wxDL_VERBATIM) == true) {
1231+ for (i = 0; gssapi_symlist[i].symname != NULL; i++)
1232+ {
1233+ *gssapi_symlist[i].symptr =
1234+ gsslib.GetSymbol(gssapi_symlist[i].symname);
1235+
1236+ if (! *gssapi_symlist[i].symptr) {
1237+ gsslib.Unload();
1238+ gssloaderr =
1239+ wxString::Format("Unable to load "
1240+ "symbol %s from "
1241+ "library %s",
1242+ gssapi_symlist[i].symname,
1243+ gsslibname);
1244+ return false;
1245+ }
1246+ }
1247+ } else {
1248+ gssloaderr = wxString::Format("Load of %s failed", gsslibname);
1249+ return false;
1250+ }
1251+
1252+ return true;
1253+}
1254+
1255+wxString GetGssLibraryName(void)
1256+{
1257+ return gsslibname;
1258+}
1259+
1260+wxString GetGssLoadError(void)
1261+{
1262+ return gssloaderr;
1263+}
1264+
1265+bool GssInitSecContext(CControlSocket *psocket, void **context, void *name,
1266+ bool delegate, const wxString &input_token,
1267+ wxString *output_token, bool *complete)
1268+{
1269+ OM_uint32 maj_stat, min_stat;
1270+ gss_buffer_desc recv_token = GSS_C_EMPTY_BUFFER,
1271+ send_token = GSS_C_EMPTY_BUFFER;
1272+ wxMemoryBuffer decoded_recv;
1273+
1274+ if (! p_gss_init_sec_context)
1275+ {
1276+ psocket->LogMessage(MessageType::Error, _("Internal Error: p_gss_init_sec_context is NULL!"));
1277+ return false;
1278+ }
1279+
1280+ if (input_token.Length() > 0) {
1281+ decoded_recv = wxBase64Decode(input_token);
1282+ if (!decoded_recv) {
1283+ psocket->LogMessage(MessageType::Error, _("Unable to decode base64 data when calling gss_init_sec_context"));
1284+ return false;
1285+ }
1286+
1287+ recv_token.value = decoded_recv.GetData();
1288+ recv_token.length = decoded_recv.GetDataLen();
1289+ }
1290+
1291+ maj_stat = p_gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL,
1292+ (gss_ctx_id_t *) context,
1293+ (gss_name_t) name,
1294+ GSS_C_NO_OID,
1295+ GSS_C_MUTUAL_FLAG |
1296+ GSS_C_REPLAY_FLAG |
1297+ (delegate ? GSS_C_DELEG_FLAG : 0), 0,
1298+ GSS_C_NO_CHANNEL_BINDINGS,
1299+ &recv_token, NULL, &send_token,
1300+ NULL, NULL);
1301+
1302+ if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
1303+ {
1304+ LogGssError(psocket, maj_stat, min_stat);
1305+ return false;
1306+ }
1307+
1308+ if (complete)
1309+ {
1310+ *complete = (maj_stat == GSS_S_COMPLETE) ? true : false;
1311+ }
1312+
1313+ if (send_token.length > 0)
1314+ {
1315+ *output_token = wxBase64Encode((char *) send_token.value,
1316+ send_token.length);
1317+ p_gss_release_buffer(&min_stat, &send_token);
1318+ }
1319+
1320+ return true;
1321+}
1322+
1323+bool GssImportName(CControlSocket *psocket, const wxString& name,
1324+ void **target_ret)
1325+{
1326+ OM_uint32 maj_stat, min_stat;
1327+ gss_buffer_desc name_buf;
1328+
1329+ if (!p_gss_import_name || !p_GSS_C_NT_HOSTBASED_SERVICE)
1330+ {
1331+ psocket->LogMessage(MessageType::Error, _("Internal Error: p_gss_import_name or p_GSS_C_NT_HOSTBASED_SERVICE is NULL!"));
1332+ return false;
1333+ }
1334+
1335+ std::string temp = name.ToStdString();
1336+
1337+ name_buf.value = (char *) temp.c_str();
1338+ name_buf.length = name.Length();
1339+
1340+ maj_stat = p_gss_import_name(&min_stat, &name_buf,
1341+ *p_GSS_C_NT_HOSTBASED_SERVICE,
1342+ (gss_name_t *) target_ret);
1343+
1344+ if (maj_stat != GSS_S_COMPLETE)
1345+ {
1346+ LogGssError(psocket, maj_stat, min_stat);
1347+ return false;
1348+ }
1349+
1350+ return true;
1351+}
1352+
1353+bool
1354+GssWrap(CControlSocket *psocket, void *context, bool encrypt,
1355+ const wxCharBuffer &inbuf, wxString *outbuf)
1356+{
1357+ OM_uint32 maj_stat, min_stat;
1358+ gss_buffer_desc in_wrap, out_wrap = GSS_C_EMPTY_BUFFER;
1359+ int conf_state;
1360+ wxMemoryBuffer encbuf;
1361+
1362+ if (!p_gss_wrap)
1363+ {
1364+ psocket->LogMessage(MessageType::Error, _("Internal Error: p_gss_wrap is NULL!"));
1365+ return false;
1366+ }
1367+
1368+ in_wrap.value = (char *) inbuf.data();
1369+ in_wrap.length = inbuf.length() + 1;
1370+
1371+ maj_stat = p_gss_wrap(&min_stat, (gss_ctx_id_t) context,
1372+ encrypt ? 1 : 0, GSS_C_QOP_DEFAULT, &in_wrap,
1373+ &conf_state, &out_wrap);
1374+
1375+ if (maj_stat != GSS_S_COMPLETE)
1376+ {
1377+ LogGssError(psocket, maj_stat, min_stat);
1378+ return false;
1379+ }
1380+
1381+ encbuf.AppendData(out_wrap.value, out_wrap.length);
1382+
1383+ *outbuf = wxBase64Encode(encbuf);
1384+
1385+ p_gss_release_buffer(&min_stat, &out_wrap);
1386+
1387+ return true;
1388+}
1389+
1390+bool
1391+GssWrap(CControlSocket *psocket, void *context, bool encrypt,
1392+ const unsigned char *inbuf, size_t inbuflen, unsigned char **outbuf,
1393+ size_t *outbufsize, size_t *outbuflen, size_t offset)
1394+{
1395+ OM_uint32 maj_stat, min_stat;
1396+ gss_buffer_desc in_wrap, out_wrap = GSS_C_EMPTY_BUFFER;
1397+ int conf_state;
1398+
1399+ if (!p_gss_wrap)
1400+ {
1401+ psocket->LogMessage(MessageType::Error, _("Internal Error: p_gss_wrap is NULL!"));
1402+ return false;
1403+ }
1404+
1405+ in_wrap.value = (void *) inbuf;
1406+ in_wrap.length = inbuflen;
1407+
1408+ maj_stat = p_gss_wrap(&min_stat, (gss_ctx_id_t) context,
1409+ encrypt ? 1 : 0, GSS_C_QOP_DEFAULT, &in_wrap,
1410+ &conf_state, &out_wrap);
1411+
1412+ if (maj_stat != GSS_S_COMPLETE)
1413+ {
1414+ LogGssError(psocket, maj_stat, min_stat);
1415+ return false;
1416+ }
1417+
1418+ if (out_wrap.length > *outbufsize + offset) {
1419+ *outbufsize = out_wrap.length + offset;
1420+ *outbuf = (unsigned char *) realloc(*outbuf, *outbufsize);
1421+ }
1422+
1423+ memcpy(*outbuf + offset, out_wrap.value, out_wrap.length);
1424+
1425+ *outbuflen = out_wrap.length;
1426+
1427+ p_gss_release_buffer(&min_stat, &out_wrap);
1428+
1429+ return true;
1430+}
1431+
1432+bool
1433+GssUnwrap(CControlSocket *psocket, void *context, const wxString& inbuf,
1434+ wxMemoryBuffer *outbuf, bool *encrypt)
1435+{
1436+ OM_uint32 maj_stat, min_stat;
1437+ gss_buffer_desc in_wrap, out_wrap;
1438+ int conf_level;
1439+
1440+ if (!p_gss_unwrap)
1441+ {
1442+ psocket->LogMessage(MessageType::Error, _("Internal Error: p_gss_unwrap is NULL!"));
1443+ return false;
1444+ }
1445+
1446+ wxMemoryBuffer indecbuf = wxBase64Decode(inbuf);
1447+
1448+ if (!indecbuf)
1449+ {
1450+ psocket->LogMessage(MessageType::Error, _("Unable to base64-decode message response data!"));
1451+ return false;
1452+ }
1453+
1454+ in_wrap.value = indecbuf.GetData();
1455+ in_wrap.length = indecbuf.GetDataLen();
1456+
1457+ maj_stat = p_gss_unwrap(&min_stat, (gss_ctx_id_t) context, &in_wrap,
1458+ &out_wrap, &conf_level, NULL);
1459+
1460+ if (maj_stat != GSS_S_COMPLETE)
1461+ {
1462+ LogGssError(psocket, maj_stat, min_stat);
1463+ return false;
1464+ }
1465+
1466+ if (encrypt)
1467+ {
1468+ *encrypt = conf_level != 0;
1469+ }
1470+
1471+ outbuf->Clear();
1472+ outbuf->AppendData(out_wrap.value, out_wrap.length);
1473+
1474+ p_gss_release_buffer(&min_stat, &out_wrap);
1475+
1476+ return true;
1477+}
1478+
1479+bool GssUnwrap(CControlSocket* psocket, void *context, unsigned char *inbuf,
1480+ size_t inbuflen, unsigned char **outbuf, size_t *outbuflen,
1481+ size_t *retbufsize)
1482+{
1483+ OM_uint32 maj_stat, min_stat;
1484+ gss_buffer_desc in_wrap, out_wrap;
1485+ int conf_level;
1486+
1487+ if (!p_gss_unwrap)
1488+ {
1489+ psocket->LogMessage(MessageType::Error, _("Internal Error: p_gss_unwrap is NULL!"));
1490+ return false;
1491+ }
1492+
1493+ in_wrap.value = inbuf;
1494+ in_wrap.length = inbuflen;
1495+
1496+ maj_stat = p_gss_unwrap(&min_stat, (gss_ctx_id_t) context, &in_wrap,
1497+ &out_wrap, &conf_level, NULL);
1498+
1499+ if (maj_stat != GSS_S_COMPLETE)
1500+ {
1501+ LogGssError(psocket, maj_stat, min_stat);
1502+ return false;
1503+ }
1504+
1505+ if (out_wrap.length > *outbuflen) {
1506+ *outbuf = (unsigned char *) realloc(*outbuf, out_wrap.length);
1507+ *outbuflen = out_wrap.length;
1508+ }
1509+
1510+ memcpy(*outbuf, out_wrap.value, out_wrap.length);
1511+ *retbufsize = out_wrap.length;
1512+
1513+ p_gss_release_buffer(&min_stat, &out_wrap);
1514+
1515+ return true;
1516+}
1517+
1518+bool
1519+GssWrapSizeLimit(CControlSocket *psocket, void *context, size_t output_size,
1520+ size_t *max_input_size)
1521+{
1522+ OM_uint32 maj_stat, min_stat;
1523+ OM_uint32 req_insize;
1524+
1525+ if (!p_gss_wrap_size_limit)
1526+ {
1527+ psocket->LogMessage(MessageType::Error, _("Internal Error: p_gss_wrap_size_limit is NULL!"));
1528+ return false;
1529+ }
1530+
1531+ maj_stat = p_gss_wrap_size_limit(&min_stat, (gss_ctx_id_t) context, 1,
1532+ GSS_C_QOP_DEFAULT, output_size,
1533+ &req_insize);
1534+
1535+ if (maj_stat != GSS_S_COMPLETE)
1536+ {
1537+ LogGssError(psocket, maj_stat, min_stat);
1538+ return false;
1539+ }
1540+
1541+ *max_input_size = req_insize;
1542+
1543+ return true;
1544+}
1545+
1546+void
1547+GssReleaseName(CControlSocket* psocket, void **name)
1548+{
1549+ OM_uint32 maj_stat, min_stat;
1550+
1551+ if (!p_gss_release_name)
1552+ {
1553+ psocket->LogMessage(MessageType::Error, _("Internal Error: p_gss_release_buffer is NULL!"));
1554+ return;
1555+ }
1556+
1557+ maj_stat = p_gss_release_name(&min_stat, (gss_name_t *) name);
1558+
1559+ return;
1560+}
1561+
1562+static void
1563+LogGssError(CControlSocket *psocket, OM_uint32 maj, OM_uint32 min)
1564+{
1565+ OM_uint32 maj_stat, min_stat, message_context = 0;
1566+ gss_buffer_desc status_string;
1567+
1568+ if (!p_gss_display_status)
1569+ {
1570+ psocket->LogMessage(MessageType::Error, _("Internal Error: p_gss_display_status is NULL!"));
1571+ return;
1572+ }
1573+
1574+ do {
1575+ maj_stat = p_gss_display_status(&min_stat, maj,
1576+ GSS_C_GSS_CODE, GSS_C_NO_OID,
1577+ &message_context,
1578+ &status_string);
1579+
1580+ if (maj_stat == GSS_S_COMPLETE)
1581+ {
1582+ psocket->LogMessage(MessageType::Error, _T("GSSAPI Error Major Status: %.*s"),
1583+ (int) status_string.length,
1584+ (char *) status_string.value);
1585+ p_gss_release_buffer(&min_stat, &status_string);
1586+ }
1587+ } while (message_context != 0);
1588+
1589+ do {
1590+ maj_stat = p_gss_display_status(&min_stat, min,
1591+ GSS_C_MECH_CODE, GSS_C_NO_OID,
1592+ &message_context,
1593+ &status_string);
1594+
1595+ if (maj_stat == GSS_S_COMPLETE)
1596+ {
1597+ psocket->LogMessage(MessageType::Error, _T("GSSAPI Error Minor Status: %.*s"),
1598+ (int) status_string.length,
1599+ (char *) status_string.value);
1600+ p_gss_release_buffer(&min_stat, &status_string);
1601+ }
1602+ } while (message_context != 0);
1603+}
1604diff --git a/src/engine/gsssocket.cpp b/src/engine/gsssocket.cpp
1605new file mode 100644
1606index 0000000..938b194
1607--- /dev/null
1608+++ b/src/engine/gsssocket.cpp
1609@@ -0,0 +1,300 @@
1610+#include <filezilla.h>
1611+#include "engineprivate.h"
1612+#include "gsssocket.h"
1613+#include "ControlSocket.h"
1614+#include <errno.h>
1615+
1616+CGssSocket::CGssSocket(CEventHandler* pEvtHandler, CSocket* pSocket,
1617+ CControlSocket* pOwner, void *gcontext_in,
1618+ size_t pbufSize)
1619+ : CEventHandler(pOwner->event_loop_)
1620+ , CBackend(pEvtHandler)
1621+ , m_pOwner(pOwner)
1622+ , gcontext(gcontext_in)
1623+ , p_bufSize(pbufSize)
1624+ , databuffer(NULL)
1625+ , databuffersize(0)
1626+ , datainbuffer(0)
1627+ , encbuffer(NULL)
1628+ , encbuffersize(0)
1629+ , encbufferlen(0)
1630+ , llen(4)
1631+ , endoffile(false)
1632+ , writewait(false)
1633+ , shutdown(false)
1634+{
1635+ wxASSERT(pSocket);
1636+
1637+ m_pSocket = pSocket;
1638+ m_pSocketBackend = new CSocketBackend(this, *m_pSocket, m_pOwner->GetEngine().GetRateLimiter());
1639+ GssWrapSizeLimit(m_pOwner, gcontext, p_bufSize, &maxwrap);
1640+}
1641+
1642+CGssSocket::~CGssSocket()
1643+{
1644+ delete m_pSocketBackend;
1645+ if (databuffer)
1646+ free(databuffer);
1647+ if (encbuffer)
1648+ free(encbuffer);
1649+}
1650+
1651+int
1652+CGssSocket::Read(void *buffer, unsigned int size, int& error)
1653+{
1654+ if (datainbuffer == 0 && !endoffile) {
1655+ if (!FetchData(error)) {
1656+ return -1;
1657+ }
1658+ }
1659+
1660+ if (endoffile == true)
1661+ return 0;
1662+
1663+ unsigned int retsize = datainbuffer > size ? size : datainbuffer;
1664+
1665+ memcpy(buffer, dataptr, retsize);
1666+
1667+ dataptr += retsize;
1668+ datainbuffer -= retsize;
1669+
1670+ return retsize;
1671+}
1672+
1673+/*
1674+ * Called to fill the buffer based on how much data we need
1675+ */
1676+
1677+bool CGssSocket::FetchData(int& error)
1678+{
1679+ int cc;
1680+
1681+ /*
1682+ * If llen > 0, then that means we need to read length bytes.
1683+ * If llen == 0, then that means we already have the length, it's
1684+ * in packetsize, and we can skip that.
1685+ */
1686+
1687+ if (llen > 0) {
1688+
1689+ /*
1690+ * According to RFC 2228, each packet is prefixed by 4
1691+ * bytes which contains the length of the data. We're
1692+ * just going to hardcode this as "4" right now.
1693+ */
1694+
1695+ while (llen > 0) {
1696+ cc = m_pSocketBackend->Read(lenbytes + 4 - llen,
1697+ llen, error);
1698+
1699+ if (cc == 0) {
1700+ error = ECONNABORTED;
1701+ return false;
1702+ }
1703+
1704+ if (cc < 0) {
1705+ return false;
1706+ }
1707+
1708+ llen -= cc;
1709+ }
1710+
1711+ packetsize = (lenbytes[0] << 24) + (lenbytes[1] << 16) +
1712+ (lenbytes[2] << 8) + lenbytes[3];
1713+
1714+ if (packetsize > encbuffersize) {
1715+ encbuffer = (unsigned char *) realloc(encbuffer,
1716+ packetsize);
1717+ encbuffersize = packetsize;
1718+ }
1719+
1720+ encbufferlen = 0;
1721+ }
1722+
1723+ while (encbufferlen < packetsize) {
1724+ cc = m_pSocketBackend->Read(encbuffer + encbufferlen,
1725+ packetsize - encbufferlen, error);
1726+ if (cc <= 0)
1727+ return false;
1728+
1729+ encbufferlen += cc;
1730+ }
1731+
1732+ /*
1733+ * Should be a full GSS packet of data now
1734+ */
1735+
1736+ if (!GssUnwrap(m_pOwner, gcontext, encbuffer, packetsize, &databuffer,
1737+ &databuffersize, &datainbuffer)) {
1738+ error = EIO;
1739+ return false;
1740+ }
1741+
1742+ dataptr = databuffer;
1743+ llen = 4;
1744+
1745+ /*
1746+ * An EOF is encoded by a packet with no data
1747+ */
1748+
1749+ if (datainbuffer == 0)
1750+ endoffile = true;
1751+
1752+ return true;
1753+}
1754+
1755+void CGssSocket::operator()(CEventBase const& ev)
1756+{
1757+ Dispatch<CSocketEvent>(ev, this, &CGssSocket::OnSocketEvent);
1758+}
1759+
1760+void CGssSocket::OnSocketEvent(CSocketEventSource*, SocketEventType t, int error)
1761+{
1762+ switch (t)
1763+ {
1764+ case SocketEventType::read:
1765+ {
1766+ m_pEvtHandler->SendEvent<CSocketEvent>(this, SocketEventType::read, 0);
1767+ break;
1768+ }
1769+ case SocketEventType::write:
1770+ {
1771+ int ret = 0;
1772+
1773+ if (writewait)
1774+ ret = PushData();
1775+
1776+ if (ret == 0) {
1777+ m_pEvtHandler->SendEvent<CSocketEvent>(this, SocketEventType::write, 0);
1778+ }
1779+
1780+ break;
1781+ }
1782+ default:
1783+ break;
1784+ }
1785+}
1786+
1787+int CGssSocket::PushData(void)
1788+{
1789+ int cc, error;
1790+
1791+ if (!writewait)
1792+ return 0;
1793+
1794+ while (datainbuffer > 0) {
1795+ cc = m_pSocketBackend->Write(dataptr, datainbuffer, error);
1796+
1797+ if (cc < 0) {
1798+ if (error != EAGAIN)
1799+ endoffile = true;
1800+ return error;
1801+ }
1802+
1803+ if (cc == 0) {
1804+ return EIO;
1805+ }
1806+
1807+ dataptr += cc;
1808+ datainbuffer -= cc;
1809+ }
1810+
1811+ if (datainbuffer == 0)
1812+ writewait = false;
1813+
1814+ return 0;
1815+}
1816+
1817+int CGssSocket::Write(const void *buffer, unsigned int len, int& error)
1818+{
1819+ size_t bytestowrite;
1820+ int cc;
1821+
1822+ if (endoffile) {
1823+ error = ENOTCONN;
1824+ return -1;
1825+ }
1826+
1827+ if (writewait) {
1828+ error = EAGAIN;
1829+ return -1;
1830+ }
1831+
1832+ bytestowrite = len > maxwrap ? maxwrap : len;
1833+
1834+ if (!GssWrap(m_pOwner, gcontext, true, (unsigned char *) buffer,
1835+ bytestowrite, &databuffer, &databuffersize,
1836+ &datainbuffer, 4)) {
1837+ error = EIO;
1838+ return -1;
1839+ }
1840+
1841+ /*
1842+ * Prefix the buffer with the length, and bump our length to include
1843+ * the buffer prefix.
1844+ */
1845+
1846+ databuffer[0] = (datainbuffer >> 24) & 0xff;
1847+ databuffer[1] = (datainbuffer >> 16) & 0xff;
1848+ databuffer[2] = (datainbuffer >> 8) & 0xff;
1849+ databuffer[3] = datainbuffer & 0xff;
1850+
1851+ datainbuffer += 4;
1852+
1853+ dataptr = databuffer;
1854+
1855+ while (datainbuffer > 0) {
1856+ cc = m_pSocketBackend->Write(dataptr, datainbuffer, error);
1857+
1858+ if (cc <= 0) {
1859+ if (cc < 0 && error != EAGAIN)
1860+ return -1;
1861+ writewait = true;
1862+ break;
1863+ }
1864+
1865+ dataptr += cc;
1866+ datainbuffer -= cc;
1867+ }
1868+
1869+ /*
1870+ * If data is queued by us, we need to tell the upper layers that
1871+ * we've "written" it all, so we don't get it again. So always
1872+ * return bytestowrite at this point.
1873+ */
1874+
1875+ return bytestowrite;
1876+}
1877+
1878+int CGssSocket::Shutdown(void)
1879+{
1880+ int error;
1881+
1882+ if (shutdown && writewait)
1883+ return EAGAIN;
1884+
1885+ /*
1886+ * At the end of our write, we should encode a GSS packet with
1887+ * a zero length.
1888+ */
1889+
1890+ shutdown = true;
1891+
1892+ if (Write(NULL, 0, error) < 0)
1893+ return error;
1894+
1895+ return 0;
1896+}
1897+
1898+/*
1899+ * All stubs for now
1900+ */
1901+
1902+void CGssSocket::OnRateAvailable(enum CRateLimiter::rate_direction)
1903+{
1904+}
1905+
1906+int CGssSocket::Peek(void *buffer, unsigned int len, int& error)
1907+{
1908+ return 0;
1909+}
1910diff --git a/src/engine/gsssocket.h b/src/engine/gsssocket.h
1911new file mode 100644
1912index 0000000..2017724
1913--- /dev/null
1914+++ b/src/engine/gsssocket.h
1915@@ -0,0 +1,53 @@
1916+#ifndef __GSSSOCKET_H__
1917+#define __GSSSOCKET_H__
1918+
1919+#include "gssinterface.h"
1920+#include "backend.h"
1921+#include "socket.h"
1922+
1923+class CControlSocket;
1924+class CGssSocket : protected CEventHandler, public CBackend
1925+{
1926+public:
1927+ CGssSocket(CEventHandler *pEvtHandler, CSocket *psocket,
1928+ CControlSocket *pOwner, void *gcontext_in, size_t pbsz);
1929+ virtual ~CGssSocket();
1930+
1931+ virtual int Read(void *buffer, unsigned int size, int& error);
1932+ virtual int Peek(void *buffer, unsigned int size, int& error);
1933+ virtual int Write(const void *buffer, unsigned int size, int& error);
1934+ virtual int Shutdown(void);
1935+
1936+protected:
1937+ bool FetchData(int& error);
1938+ int PushData(void);
1939+ void OnSocketEvent(CSocketEventSource*, SocketEventType, int);
1940+ virtual void OnRateAvailable(enum CRateLimiter::rate_direction direction);
1941+ virtual void operator()(CEventBase const& ev);
1942+
1943+ CSocketBackend* m_pSocketBackend;
1944+ CControlSocket* m_pOwner;
1945+ CSocket* m_pSocket;
1946+ void *gcontext;
1947+ size_t p_bufSize; /* PBSZ from connection */
1948+ size_t maxwrap; /* Max size of data to gss_wrap */
1949+
1950+ /*
1951+ * We use "databuffer" to hold network data for reading and writing
1952+ */
1953+
1954+ unsigned char *databuffer; /* Decrypted data buffer */
1955+ unsigned char *dataptr; /* Ptr to next data to be read */
1956+ size_t databuffersize; /* Size of databuffer */
1957+ size_t datainbuffer; /* # of bytes in databuffer */
1958+ unsigned int packetsize; /* Size of encrypted packet */
1959+ unsigned char *encbuffer; /* Encrypted buffer */
1960+ size_t encbuffersize; /* Size of encrypted buffer */
1961+ size_t encbufferlen; /* # of bytes in encrypted buffer */
1962+ unsigned char lenbytes[4]; /* Buffer for length bytes */
1963+ unsigned int llen; /* Remaining bytes to fill lenbytes */
1964+ bool endoffile; /* Set to true if the net is closed */
1965+ bool writewait; /* True if waiting to write */
1966+ bool shutdown; /* True if shutting down */
1967+};
1968+#endif /* __GSSSOCKET_H__ */
1969diff --git a/src/engine/transfersocket.cpp b/src/engine/transfersocket.cpp
1970index 65d79e6..af08d3d 100644
1971--- a/src/engine/transfersocket.cpp
1972+++ b/src/engine/transfersocket.cpp
1973@@ -5,6 +5,7 @@
1974 #include "iothread.h"
1975 #include "optionsbase.h"
1976 #include "tlssocket.h"
1977+#include "gsssocket.h"
1978 #include "transfersocket.h"
1979 #include "proxy.h"
1980 #include "servercapabilities.h"
1981@@ -39,12 +40,17 @@ void CTransferSocket::ResetSocket()
1982 if( m_pBackend == m_pTlsSocket ) {
1983 m_pBackend = 0;
1984 }
1985+ if (m_pBackend == m_pGssSocket) {
1986+ m_pBackend = 0;
1987+ }
1988 delete m_pTlsSocket;
1989+ delete m_pGssSocket;
1990 delete m_pBackend;
1991 delete m_pSocketServer;
1992 delete m_pSocket;
1993 m_pProxyBackend = 0;
1994 m_pTlsSocket = 0;
1995+ m_pGssSocket = 0;
1996 m_pBackend = 0;
1997 m_pSocketServer = 0;
1998 m_pSocket = 0;
1999@@ -430,6 +436,13 @@ void CTransferSocket::OnClose(int error)
2000 else
2001 TransferEnd(TransferEndReason::successful);
2002 }
2003+ else if (m_shutdown && m_pGssSocket)
2004+ {
2005+ if (m_pGssSocket->Shutdown() != 0)
2006+ TransferEnd(TransferEndReason::transfer_failure);
2007+ else
2008+ TransferEnd(TransferEndReason::successful);
2009+ }
2010 else
2011 TransferEnd(TransferEndReason::transfer_failure);
2012 return;
2013@@ -637,10 +650,12 @@ bool CTransferSocket::CheckGetNextReadBuffer()
2014 return false;
2015 }
2016 else if (res == IO_Success) {
2017- if (m_pTlsSocket) {
2018+ if (m_pTlsSocket || m_pGssSocket) {
2019 m_shutdown = true;
2020
2021- int error = m_pTlsSocket->Shutdown();
2022+ int error = m_pTlsSocket ?
2023+ m_pTlsSocket->Shutdown() :
2024+ m_pGssSocket->Shutdown();
2025 if (error != 0) {
2026 if (error != EAGAIN)
2027 TransferEnd(TransferEndReason::transfer_failure);
2028@@ -714,6 +729,16 @@ bool CTransferSocket::InitTls(const CTlsSocket* pPrimaryTlsSocket)
2029 return true;
2030 }
2031
2032+bool CTransferSocket::InitGss()
2033+{
2034+ m_pGssSocket = new CGssSocket(this, m_pSocket, &controlSocket_,
2035+ controlSocket_.gcontext,
2036+ controlSocket_.m_protBufferSize);
2037+
2038+ m_pBackend = m_pGssSocket;
2039+ return true;
2040+}
2041+
2042 void CTransferSocket::TriggerPostponedEvents()
2043 {
2044 wxASSERT(m_bActive);
2045@@ -742,7 +767,13 @@ bool CTransferSocket::InitBackend()
2046 return true;
2047
2048 if (controlSocket_.m_protectDataChannel) {
2049- if (!InitTls(controlSocket_.m_pTlsSocket))
2050+ bool ret;
2051+ if (controlSocket_.gcontext) {
2052+ ret = InitGss();
2053+ } else {
2054+ ret = InitTls(controlSocket_.m_pTlsSocket);
2055+ }
2056+ if (!ret)
2057 return false;
2058 }
2059 else
2060diff --git a/src/engine/transfersocket.h b/src/engine/transfersocket.h
2061index ec537c7..46e49d5 100644
2062--- a/src/engine/transfersocket.h
2063+++ b/src/engine/transfersocket.h
2064@@ -19,6 +19,7 @@ enum class TransferMode
2065
2066 class CIOThread;
2067 class CTlsSocket;
2068+class CGssSocket;
2069 class CTransferSocket final : public CEventHandler
2070 {
2071 public:
2072@@ -47,6 +48,7 @@ protected:
2073
2074 bool InitBackend();
2075 bool InitTls(const CTlsSocket* pPrimaryTlsSocket);
2076+ bool InitGss();
2077
2078 void ResetSocket();
2079
2080@@ -98,6 +100,8 @@ protected:
2081 CTlsSocket* m_pTlsSocket{};
2082 bool m_shutdown{};
2083
2084+ CGssSocket* m_pGssSocket{};
2085+
2086 // Needed for the madeProgress field in CTransferStatus
2087 // Initially 0, 2 if made progress
2088 // On uploads, 1 after first WSAE_WOULDBLOCK
2089diff --git a/src/include/Makefile.am b/src/include/Makefile.am
2090index 60a3e46..8fad5a3 100644
2091--- a/src/include/Makefile.am
2092+++ b/src/include/Makefile.am
2093@@ -10,6 +10,7 @@ noinst_HEADERS = \
2094 event_loop.h \
2095 externalipresolver.h \
2096 FileZillaEngine.h \
2097+ gssinterface.h \
2098 libfilezilla.h \
2099 local_filesys.h \
2100 local_path.h \
2101diff --git a/src/include/gssinterface.h b/src/include/gssinterface.h
2102new file mode 100644
2103index 0000000..c90bc0c
2104--- /dev/null
2105+++ b/src/include/gssinterface.h
2106@@ -0,0 +1,142 @@
2107+#ifndef __GSSINTERFACE_H__
2108+#define __GSSINTERFACE_H__
2109+
2110+class CControlSocket;
2111+
2112+/*
2113+ * Return 'true' if the GSSAPI library has loaded successfully. Will
2114+ * attempt to load it if it is not already loaded.
2115+ */
2116+
2117+bool IsGssLoaded(void);
2118+
2119+/*
2120+ * Load the GSS Library. If one is currently loaded, unload the old library
2121+ * first. Returns 'true' if load was successful, 'false' if not.
2122+ */
2123+
2124+bool LoadGssLibrary(const wxString &);
2125+
2126+/*
2127+ * Return the name of the currently configured GSS library
2128+ */
2129+
2130+wxString GetGssLibraryName(void);
2131+
2132+/*
2133+ * Return the last error we got when we tried to load the module
2134+ */
2135+
2136+wxString GetGssLoadError(void);
2137+
2138+/*
2139+ * Import a name via gss_import_name(). Takes target name (service@host)
2140+ * as input, returns a gss_name_t. Returns false on failure.
2141+ */
2142+
2143+bool GssImportName(CControlSocket *psocket, const wxString& , void **);
2144+
2145+/*
2146+ * Call gss_init_sec_context. Arguments are:
2147+ *
2148+ * psocket - Connection control socket (for logging)
2149+ * context - Returned/modified gss_ctx_id_t. Should be NULL at first
2150+ * call.
2151+ * name - Target name (from gss_import_name)
2152+ * delegate - if 'true', delegate credentials
2153+ * input_token - Base64-encoded input token
2154+ * output_token - Base64-encoded output token
2155+ * complete - Return 'true' if function returned GSS_S_COMPLETE
2156+ *
2157+ * Returns false on failure.
2158+ */
2159+
2160+bool GssInitSecContext(CControlSocket* psocket, void **context,
2161+ void *name, bool delegate, const wxString &input_token,
2162+ wxString *output_token, bool *complete);
2163+
2164+/*
2165+ * Call gss_wrap on data to perform encryption/integrity protection.
2166+ * Arguments are:
2167+ *
2168+ * psocket - Connection control socket (for logging)
2169+ * context - gss_ctx_id_t from gss_init_sec_context.
2170+ * encrypt - set to 'true' if performing encryption.
2171+ * inbuf - Input data
2172+ * outbuf - Base64-encoded output data
2173+ *
2174+ * Returns false on failure.
2175+ */
2176+
2177+bool GssWrap(CControlSocket* psocket, void *context, bool encrypt,
2178+ const wxCharBuffer& inbuf, wxString *outbuf);
2179+
2180+/*
2181+ * Alternate version of GssWrap that does not perform any base64 encoding
2182+ *
2183+ * Similar arguments as above, except that outbuf can be reallocated if
2184+ * it is not big enough (buffer size is passed in via *outbufsize).
2185+ *
2186+ * Offset is a "buffer offset" in case you need to do something like prepend
2187+ * length
2188+ */
2189+
2190+bool GssWrap(CControlSocket* psocket, void *context, bool encrypt,
2191+ const unsigned char *inbuf, size_t inbuflen,
2192+ unsigned char **outbuf, size_t *outbufsize, size_t *outbuflen,
2193+ size_t offset);
2194+
2195+/*
2196+ * Call gss_unwrap on data to perform decryption/integrity verification.
2197+ * Arguments are:
2198+ *
2199+ * psocket - Connection control socket (for logging)
2200+ * context - gss_ctx_id_t from gss_init_sec_context.
2201+ * inbuf - Base64-encoded input data
2202+ * outbuf - Output data
2203+ * encrypted - Will be set to 'true' if data was encrypted
2204+ *
2205+ * Returns false on failure.
2206+ */
2207+
2208+bool GssUnwrap(CControlSocket* psocket, void *context, const wxString& inbuf,
2209+ wxMemoryBuffer *outbuf, bool *encrypted);
2210+
2211+/*
2212+ * Alternate version of gss_unwrap which does not perform base64 decoding.
2213+ *
2214+ * Similar arguments to above function, except outbuf can be reallocated
2215+ * if size isn't big enough.
2216+ */
2217+
2218+bool GssUnwrap(CControlSocket* psocket, void *context, unsigned char *inbuf,
2219+ size_t inbuflen, unsigned char **outbuf, size_t *outbuflen,
2220+ size_t *retbufsize);
2221+
2222+/*
2223+ * Call gss_release_name to release a name buffer
2224+ * Arguments are:
2225+ *
2226+ * psocket - Connection control socket (for logging)
2227+ * name - Pointer to name buffer to release
2228+ *
2229+ */
2230+
2231+void GssReleaseName(CControlSocket* psocket, void **name);
2232+
2233+/*
2234+ * Call gss_wrap_size_limit to determine the maximum amount of unencrypted
2235+ * data we can send given an encrypted buffer size.
2236+ * Arguments are:
2237+
2238+ * psocket - Connection control socket (for logging)
2239+ * context - gss_ctx_id_t from gss_init_sec_context.
2240+ * output_size - Requested output size
2241+ * max_input_size - Maximum input size
2242+ *
2243+ * Returns false on failure.
2244+ */
2245+
2246+bool GssWrapSizeLimit(CControlSocket* psocket, void *context,
2247+ size_t output_size, size_t *max_input_size);
2248+#endif /* __GSSINTERFACE_H__ */
2249diff --git a/src/include/option_change_event_handler.h b/src/include/option_change_event_handler.h
2250index 3ebb6fd..37734c7 100644
2251--- a/src/include/option_change_event_handler.h
2252+++ b/src/include/option_change_event_handler.h
2253@@ -8,7 +8,7 @@
2254 class COptions;
2255
2256 enum {
2257- changed_options_size = 128
2258+ changed_options_size = 256
2259 };
2260
2261 typedef std::bitset<changed_options_size> changed_options_t;
2262diff --git a/src/include/optionsbase.h b/src/include/optionsbase.h
2263index 8b3266d..9e668f4 100644
2264--- a/src/include/optionsbase.h
2265+++ b/src/include/optionsbase.h
2266@@ -33,6 +33,11 @@ enum engineOptions
2267
2268 OPTION_ALLOW_TRANSFERMODEFALLBACK, // If PORT fails, allow PASV and vice versa
2269
2270+ OPTION_GSS_ENABLE,
2271+ OPTION_GSS_LIBRARY,
2272+ OPTION_GSS_ENCRYPTDATACHAN,
2273+ OPTION_GSS_PROTBUFSIZE,
2274+
2275 OPTION_RECONNECTCOUNT,
2276 OPTION_RECONNECTDELAY,
2277
2278diff --git a/src/interface/Makefile.am b/src/interface/Makefile.am
2279index 91b2f00..94e7e9b 100644
2280--- a/src/interface/Makefile.am
2281+++ b/src/interface/Makefile.am
2282@@ -80,6 +80,7 @@ filezilla_SOURCES = aboutdialog.cpp \
2283 settings/optionspage_filelists.cpp \
2284 settings/optionspage_filetype.cpp \
2285 settings/optionspage_ftpproxy.cpp \
2286+ settings/optionspage_gssauth.cpp \
2287 settings/optionspage_interface.cpp \
2288 settings/optionspage_language.cpp \
2289 settings/optionspage_logging.cpp \
2290diff --git a/src/interface/Options.cpp b/src/interface/Options.cpp
2291index f718614..b75acdb 100644
2292--- a/src/interface/Options.cpp
2293+++ b/src/interface/Options.cpp
2294@@ -76,6 +76,10 @@ static const t_Option options[OPTIONS_NUM] =
2295 { "Logging Raw Listing", number, _T("0"), normal },
2296 { "fzsftp executable", string, _T(""), internal },
2297 { "Allow transfermode fallback", number, _T("1"), normal },
2298+ { "Enable GSSAPI Authentication", number, _T("1"), normal },
2299+ { "GSSAPI library name", string, _T(""), normal },
2300+ { "GSSAPI data channel encrypt", number, _T("0"), normal },
2301+ { "GSSAPI protection buffer size", number, _T("65536"), normal },
2302 { "Reconnect count", number, _T("2"), normal },
2303 { "Reconnect delay", number, _T("5"), normal },
2304 { "Enable speed limits", number, _T("0"), normal },
2305diff --git a/src/interface/resources/xrc/settings.xrc b/src/interface/resources/xrc/settings.xrc
2306index c4b9af6..f5b116e 100644
2307--- a/src/interface/resources/xrc/settings.xrc
2308+++ b/src/interface/resources/xrc/settings.xrc
2309@@ -607,6 +607,109 @@
2310 </object>
2311 </object>
2312 </object>
2313+ <object class="wxPanel" name="ID_SETTINGS_CONNECTION_FTP_GSSAPI">
2314+ <object class="wxBoxSizer">
2315+ <orient>wxVERTICAL</orient>
2316+ <object class="sizeritem">
2317+ <object class="wxStaticBoxSizer">
2318+ <label>GSSAPI Authentication</label>
2319+ <orient>wxVERTICAL</orient>
2320+ <object class="sizeritem">
2321+ <flag>wxGROW</flag>
2322+ <object class="wxFlexGridSizer">
2323+ <orient>wxVERTICAL</orient>
2324+ <cols>1</cols>
2325+ <vgap>5</vgap>
2326+ <growablecols>0</growablecols>
2327+ <object class="sizeritem">
2328+ <object class="wxCheckBox" name="ID_ENABLEGSSAPI">
2329+ <label>Enable GSSAPI Authentication</label>
2330+ </object>
2331+ </object>
2332+ <object class="sizeritem">
2333+ <flag>wxGROW</flag>
2334+ <object class="wxBoxSizer">
2335+ <orient>wxHORIZONTAL</orient>
2336+ <hgap>3</hgap>
2337+ <object class="sizeritem">
2338+ <object class="wxStaticText">
2339+ <label>GSSAPI Plugin:</label>
2340+ </object>
2341+ <flag>wxALIGN_CENTER_VERTICAL</flag>
2342+ </object>
2343+ <object class="sizeritem">
2344+ <object class="wxTextCtrl" name="ID_GSSPLUGINNAME"/>
2345+ <flag>wxALIGN_CENTRE_VERTICAL|wxGROW</flag>
2346+ <option>1</option>
2347+ </object>
2348+ <object class="sizeritem">
2349+ <object class="wxButton" name="ID_BROWSEGSSPLUGIN">
2350+ <label>&amp;Browse...</label>
2351+ </object>
2352+ <flag>wxALIGN_CENTRE_VERTICAL</flag>
2353+ </object>
2354+ <flag>wxGROW</flag>
2355+ </object>
2356+ </object>
2357+ <object class="sizeritem">
2358+ <flag>wxGROW</flag>
2359+ <object class="wxBoxSizer">
2360+ <orient>wxHORIZONTAL</orient>
2361+ <object class="sizeritem">
2362+ <object class="wxStaticText">
2363+ <label>Module status:</label>
2364+ </object>
2365+ </object>
2366+ <object class="sizeritem">
2367+ <flag>wxGROW</flag>
2368+ <option>1</option>
2369+ <object class="wxStaticText" name="ID_GSSMODSTATUS"/>
2370+ </object>
2371+ </object>
2372+ </object>
2373+ <object class="sizeritem">
2374+ <object class="wxStaticText">
2375+ <label>Note: the following settings only apply to new connections, not existing ones</label>
2376+ </object>
2377+ </object>
2378+ <object class="sizeritem">
2379+ <object class="wxCheckBox" name="ID_ENABLEGSSDATAENCRYPT">
2380+ <label>Enable Data Channel Encryption</label>
2381+ </object>
2382+ </object>
2383+ <object class="sizeritem">
2384+ <object class="wxFlexGridSizer">
2385+ <cols>3</cols>
2386+ <object class="sizeritem">
2387+ <object class="wxStaticText">
2388+ <label>Encrypted buffer size:</label>
2389+ </object>
2390+ <flag>wxALIGN_CENTRE_VERTICAL</flag>
2391+ </object>
2392+ <object class="sizeritem">
2393+ <object class="wxTextCtrl" name="ID_GSSPROTBUFSIZE">
2394+ <size>34,-1d</size>
2395+ </object>
2396+ <flag>wxALIGN_CENTRE_VERTICAL</flag>
2397+ </object>
2398+ <hgap>4</hgap>
2399+ <object class="sizeritem">
2400+ <object class="wxStaticText">
2401+ <label>(32768-1048576)</label>
2402+ </object>
2403+ <flag>wxALIGN_CENTRE_VERTICAL</flag>
2404+ </object>
2405+ </object>
2406+ </object>
2407+ </object>
2408+ <flag>wxLEFT|wxRIGHT|wxBOTTOM</flag>
2409+ <border>4</border>
2410+ </object>
2411+ </object>
2412+ <flag>wxGROW</flag>
2413+ </object>
2414+ </object>
2415+ </object>
2416 <object class="wxPanel" name="ID_SETTINGS_CONNECTION_SFTP">
2417 <object class="wxFlexGridSizer">
2418 <cols>2</cols>
2419diff --git a/src/interface/settings/optionspage_gssauth.cpp b/src/interface/settings/optionspage_gssauth.cpp
2420new file mode 100644
2421index 0000000..9f96e5f
2422--- /dev/null
2423+++ b/src/interface/settings/optionspage_gssauth.cpp
2424@@ -0,0 +1,129 @@
2425+#include <filezilla.h>
2426+
2427+#include "../Options.h"
2428+#include "settingsdialog.h"
2429+#include "optionspage.h"
2430+#include "optionspage_gssauth.h"
2431+#include "gssinterface.h"
2432+
2433+BEGIN_EVENT_TABLE(COptionsPageGssAuth, COptionsPageGssAuth::COptionsPage)
2434+EVT_CHECKBOX(XRCID("ID_ENABLEGSSAPI"), COptionsPageGssAuth::OnGssSettingChanged)
2435+EVT_CHECKBOX(XRCID("ID_ENABLEGSSDATAENCRYPT"), COptionsPageGssAuth::OnGssSettingChanged)
2436+EVT_BUTTON(XRCID("ID_BROWSEGSSPLUGIN"), COptionsPageGssAuth::OnBrowse)
2437+END_EVENT_TABLE()
2438+
2439+bool COptionsPageGssAuth::LoadPage()
2440+{
2441+ bool failure = false;
2442+ wxString libname;
2443+
2444+ SetCheckFromOption(XRCID("ID_ENABLEGSSAPI"), OPTION_GSS_ENABLE,
2445+ failure);
2446+
2447+ libname = m_pOptions->GetOption(OPTION_GSS_LIBRARY);
2448+
2449+ if (libname.IsEmpty()) {
2450+ libname = GetGssLibraryName();
2451+ }
2452+
2453+ SetText(XRCID("ID_GSSPLUGINNAME"), libname, failure);
2454+
2455+ SetCheckFromOption(XRCID("ID_ENABLEGSSDATAENCRYPT"),
2456+ OPTION_GSS_ENCRYPTDATACHAN, failure);
2457+
2458+ SetTextFromOption(XRCID("ID_GSSPROTBUFSIZE"), OPTION_GSS_PROTBUFSIZE,
2459+ failure);
2460+
2461+ if (!failure)
2462+ SetCtrlState();
2463+
2464+ return !failure;
2465+}
2466+
2467+bool COptionsPageGssAuth::SavePage()
2468+{
2469+ SetOptionFromCheck(XRCID("ID_ENABLEGSSAPI"), OPTION_GSS_ENABLE);
2470+ SetOptionFromText(XRCID("ID_GSSPLUGINNAME"), OPTION_GSS_LIBRARY);
2471+ SetOptionFromCheck(XRCID("ID_ENABLEGSSDATAENCRYPT"),
2472+ OPTION_GSS_ENCRYPTDATACHAN);
2473+ SetOptionFromText(XRCID("ID_GSSPROTBUFSIZE"), OPTION_GSS_PROTBUFSIZE);
2474+
2475+ return true;
2476+}
2477+
2478+bool COptionsPageGssAuth::Validate()
2479+{
2480+ wxTextCtrl *pTextCtrl = XRCCTRL(*this, "ID_GSSPLUGINNAME", wxTextCtrl);
2481+
2482+ if (pTextCtrl->IsModified()) {
2483+ pTextCtrl->SetModified(false);
2484+ LoadGssLibrary(pTextCtrl->GetValue());
2485+ }
2486+
2487+ wxTextCtrl *pProtBufSz = XRCCTRL(*this, "ID_GSSPROTBUFSIZE", wxTextCtrl);
2488+
2489+ long protbufsz;
2490+
2491+ if (!pProtBufSz->GetValue().ToLong(&protbufsz) ||
2492+ protbufsz < 32768 || protbufsz > 1048576)
2493+ return DisplayError(pProtBufSz, _("Encryption buffer size must be between 32768 and 1048576."));
2494+
2495+ return true;
2496+}
2497+
2498+void COptionsPageGssAuth::OnBrowse(wxCommandEvent& event)
2499+{
2500+ wxFileDialog dlg(this, _("Select GSS library"), wxString(),
2501+ GetGssLibraryName(),
2502+#ifdef __WXMSW__
2503+ _T("Dynamic link library (*.dll)|*.dll"),
2504+#elif __WXMAC__
2505+ _T("Shared library (*.dylib)|*.dylib;public.unix-executable"),
2506+#else
2507+ wxFileSelectorDefaultWildcardStr,
2508+#endif
2509+ wxFD_OPEN | wxFD_FILE_MUST_EXIST);
2510+
2511+ if (dlg.ShowModal() != wxID_OK)
2512+ return;
2513+
2514+ XRCCTRL(*this, "ID_GSSPLUGINNAME", wxTextCtrl)->ChangeValue(dlg.GetPath());
2515+
2516+ XRCCTRL(*this, "ID_GSSPLUGINNAME", wxTextCtrl)->SetModified(true);
2517+
2518+ SetCtrlState();
2519+}
2520+
2521+void COptionsPageGssAuth::OnGssSettingChanged(wxCommandEvent& event)
2522+{
2523+ SetCtrlState();
2524+}
2525+
2526+void COptionsPageGssAuth::SetCtrlState()
2527+{
2528+ bool enabled = XRCCTRL(*this, "ID_ENABLEGSSAPI", wxCheckBox)->GetValue();
2529+ wxTextCtrl *pTextCtrl = XRCCTRL(*this, "ID_GSSPLUGINNAME", wxTextCtrl);
2530+ pTextCtrl->Enable(enabled);
2531+
2532+ XRCCTRL(*this, "ID_BROWSEGSSPLUGIN", wxButton)->Enable(enabled);
2533+
2534+ if (!enabled) {
2535+ XRCCTRL(*this, "ID_GSSMODSTATUS", wxStaticText)->SetLabel(_("Disabled"));
2536+ } else {
2537+ if (pTextCtrl->IsModified()) {
2538+ pTextCtrl->SetModified(false);
2539+ LoadGssLibrary(pTextCtrl->GetValue());
2540+ }
2541+ if (IsGssLoaded()) {
2542+ XRCCTRL(*this, "ID_GSSMODSTATUS", wxStaticText)->SetLabel(_("Loaded successfully"));
2543+ } else {
2544+ XRCCTRL(*this, "ID_GSSMODSTATUS", wxStaticText)->SetLabel(GetGssLoadError());
2545+ }
2546+ }
2547+
2548+ wxCheckBox *pDataEncCtrl = XRCCTRL(*this, "ID_ENABLEGSSDATAENCRYPT", wxCheckBox);
2549+ bool enabledDataEnc = pDataEncCtrl->GetValue();
2550+ pDataEncCtrl->Enable(enabled);
2551+
2552+ XRCCTRL(*this, "ID_GSSPROTBUFSIZE", wxTextCtrl)->Enable(enabled && enabledDataEnc);
2553+}
2554diff --git a/src/interface/settings/optionspage_gssauth.h b/src/interface/settings/optionspage_gssauth.h
2555new file mode 100644
2556index 0000000..2e6b17a
2557--- /dev/null
2558+++ b/src/interface/settings/optionspage_gssauth.h
2559@@ -0,0 +1,18 @@
2560+#ifndef __OPTIONSPAGE_GSSAUTH_H__
2561+#define __OPTIONSPAGE_GSSAUTH_H__
2562+
2563+class COptionsPageGssAuth : public COptionsPage
2564+{
2565+public:
2566+ virtual wxString GetResourceName() { return _T("ID_SETTINGS_CONNECTION_FTP_GSSAPI"); }
2567+ virtual bool LoadPage();
2568+ virtual bool SavePage();
2569+ virtual bool Validate();
2570+
2571+protected:
2572+ DECLARE_EVENT_TABLE()
2573+ void OnGssSettingChanged(wxCommandEvent& event);
2574+ void OnBrowse(wxCommandEvent& event);
2575+ void SetCtrlState();
2576+};
2577+#endif //__OPTIONSPAGE_GSSAUTH_H__
2578diff --git a/src/interface/settings/settingsdialog.cpp b/src/interface/settings/settingsdialog.cpp
2579index 69490b6..4d0611f 100644
2580--- a/src/interface/settings/settingsdialog.cpp
2581+++ b/src/interface/settings/settingsdialog.cpp
2582@@ -7,6 +7,7 @@
2583 #include "optionspage_connection_active.h"
2584 #include "optionspage_connection_passive.h"
2585 #include "optionspage_ftpproxy.h"
2586+#include "optionspage_gssauth.h"
2587 #include "optionspage_connection_sftp.h"
2588 #include "optionspage_filetype.h"
2589 #include "optionspage_fileexists.h"
2590@@ -94,6 +95,7 @@ bool CSettingsDialog::LoadPages()
2591 AddPage(_("Active mode"), new COptionsPageConnectionActive, 2);
2592 AddPage(_("Passive mode"), new COptionsPageConnectionPassive, 2);
2593 AddPage(_("FTP Proxy"), new COptionsPageFtpProxy, 2);
2594+ AddPage(_("GSSAPI Auth"), new COptionsPageGssAuth, 2);
2595 AddPage(_("SFTP"), new COptionsPageConnectionSFTP, 1);
2596 AddPage(_("Generic proxy"), new COptionsPageProxy, 1);
2597 AddPage(_("Transfers"), new COptionsPageTransfer, 0);