Archive | Computer

09 März 2010 ~ 0 Comments | ÄHNLICHE ARTIKEL

[Update] hMailServer-Konfiguration via CGI

Es ist endlich geschafft! Nachdem ich die letzten Tage überlegt habe, wie ich das ganze am unkompliziertesten löse, habe ich nun eine Möglichkeit gefunden: Die Rede ist von der Verwaltung eines hMailServer Mailaccounts.

In den letzten Tagen hatte ich euch VBScripte vorgestellt, mit denen ein Benutzer sein Passwort ändern oder eine Mailumleitung einrichten. Das Problem bestand bisher, diese Funktionen auch remote verfügbar zu machen – denn direkten Zugriff auf den Server wird keiner der Mailbenutzer kriegen. ;-)

Im Moment verwende ich immernoch einen ziemlich minimalistischen Webserver auf dem Rechner – trotzdem wollte ich die Konfiguration bereits lauffähig machen. Die einzige Möglichkeit hierfür war die Verwendung eines CGI-Scriptes.
Also habe ich mich auf die Suche gemacht, wie ich ein VBScript über CGI ansprechen und ausführen kein. Dummerweise funktionierte das Codebeispiel bei mir überhaupt nicht! Eine andere Lösung musste also her…

…und diese habe ich hier gefunden. Anstatt eines CGI-Scriptes habe ich einfach eine CGI-Anwendung in Delphi geschrieben :D ! Dadurch habe ich zum einen gelernt, wie man sowas macht und zum anderen konnte ich das ganze in einer robusten, mir bekannten Sprache erstellen.

Wo ihr ein bisschen aufpassen müsst, ist bei dem Herleiten der Dateipfade: Irgendwie habe ich da das Gefühl, dass der Server die Werte PATH_INFO, PATH_TRANSLATED und SCRIPT_NAME nicht korrekt setzt. Aber das müsste man nochmal durch das Testen mit anderen Webservern überprüfen.

Hier jedenfalls erstmal der Quelltext:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
unit MainForm;

interface

uses
  Windows,
  SysUtils,
  Messages,
  HTTPApp,
  Classes;


type
  TValueAction = (vaUnknown, vaForward, vaPassword);

  TValueRecord = record
    Action         : TValueAction;
    Domain         : String;
    Username       : String;
    Password       : String;
    ForwardAddress : String;
    NewPassword    : String;
  end;

  TMainWebModule = class(TWebModule)
    procedure MainWebModuledefaultAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  private
    { Private-Deklarationen }
    function GetApplicationOutput(AApplication : String; AParameters : TStringList) : String;
    function GetCGIPath(APathTranslated : String; APathInfo : String; AScriptName : String) : String;
    function GetResultString(AAction : TValueAction; AResult : String) : String;
    function GetWindowsPath : String;
    function ParseRequest(ARequest : TWebRequest) : TValueRecord;
  public
    { Public-Deklarationen }
  end;

var
  MainWebModule : TMainWebModule;

implementation

{$R *.xfm}

const
  CCGIFolder       = '/cgi-bin';
  CCscriptExe      = 'system32\cscript.exe';
  CForwardAction   = 'forward';
  CForwardParam    = 'forward';
  CForwardVBS      = 'changeForward.vbs';
  CNewParam        = 'new';
  CNoLogoParam     = '/nologo';
  CPasswordAction  = 'password';
  CPasswordParam   = 'password';
  CPasswordVBS     = 'changePassword.vbs';
  CPathDivider     = '\';
  CQuote           = '"';
  CQuoteEscaped    = '\"';
  CURLDivider      = '/';

// source: http://delphi.about.com/cs/adptips2001/a/bltip0201_2.htm
function TMainWebModule.GetApplicationOutput(AApplication: String; AParameters: TStringList): String;
  function GetParameterLine(AApplication : String; AParameters : TStringList) : String;
  var
    LIndex : Integer;
  begin
    Result := CQuote + StringReplace(AApplication, CQuote, CQuoteEscaped, [rfReplaceAll, rfIgnoreCase]) + CQuote;

    if (AParameters <> nil) then
    begin
      for LIndex := 0 to Pred(AParameters.Count) do
        Result := Result + #32 + CQuote + StringReplace(AParameters[LIndex], CQuote, CQuoteEscaped, [rfReplaceAll, rfIgnoreCase]) + CQuote;
    end;
  end;

  procedure ProcessMessages;
  var
    LMessage : TMsg;
  begin
    while PeekMessage(LMessage, 0, 0, 0, PM_REMOVE) do
    begin
      if (LMessage.Message <> WM_QUIT) then
      begin
        TranslateMessage(LMessage);
        DispatchMessage(LMessage);
      end
      else
        Break;
    end;
  end;
const
  CReadBuffer = 1024;
var
  LAppRunning  : DWord;
  LBuffer      : PChar;
  LBytesRead   : DWord;
  LProcessInfo : TProcessInformation;
  LReadPipe    : THandle;
  LSecurity    : TSecurityAttributes;
  LStart       : TStartUpInfo;
  LWritePipe   : THandle;
begin
  Result := '';

  LSecurity.nLength             := SizeOf(TSecurityAttributes);
  LSecurity.bInheritHandle      := true;
  LSecurity.lpSecurityDescriptor := nil;

  if CreatePipe(LReadPipe, LWritePipe, @LSecurity, 0) then
  begin
    try
      LBuffer := AllocMem(Succ(CReadBuffer));
      try
        FillChar(LStart, SizeOf(LStart), #0) ;
        LStart.cb          := SizeOf(LStart) ;
        LStart.dwFlags     := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
        LStart.hStdInput   := LReadPipe;
        LStart.hStdOutput  := LWritePipe;
        LStart.wShowWindow := SW_HIDE;

        if CreateProcess(nil, PChar(GetParameterLine(AApplication, AParameters)),
                         @LSecurity, @LSecurity, true, NORMAL_PRIORITY_CLASS, nil,
                         nil, LStart, LProcessInfo) then
        begin
          try
            repeat
              LAppRunning := WaitForSingleObject(LProcessInfo.hProcess, 100);
             
              ProcessMessages;
            until (LAppRunning <> WAIT_TIMEOUT);

            repeat
              LBytesRead := 0;
              ReadFile(LReadPipe, LBuffer[0], CReadBuffer, LBytesRead, nil) ;
              LBuffer[LBytesRead] := #0;
              OemToChar(LBuffer, LBuffer);

              Result := Result + String(LBuffer);
            until (LBytesRead < CReadBuffer);
          finally
            CloseHandle(LProcessInfo.hProcess);
            CloseHandle(LProcessInfo.hThread);
          end;
        end;
      finally
        FreeMem(LBuffer);
      end;
    finally
      CloseHandle(LReadPipe);
      CloseHandle(LWritePipe);
    end;
  end;
end;

function TMainWebModule.GetCGIPath(APathTranslated, APathInfo, AScriptName: String): String;
var
  LPosition : Integer;
begin
  Result := '';

  repeat
    LPosition := Pos(CURLDivider, APathInfo);
    if (LPosition > 0) then
      APathInfo[LPosition] := CPathDivider;
  until (LPosition <= 0);

  repeat
    LPosition := Pos(CURLDivider, AScriptName);
    if (LPosition > 0) then
      AScriptName[LPosition] := CPathDivider;
  until (LPosition <= 0);

  if (Pred(Pos(APathInfo, AScriptName) + Length(APathInfo)) = Length(AScriptName)) then
  begin
    Delete(AScriptName, Succ(Length(AScriptName) - Length(APathInfo)), Length(APathInfo));

    if (Pred(Pos(APathInfo, APathTranslated) + Length(APathInfo)) = Length(APathTranslated)) then
    begin
      Delete(APathTranslated, Succ(Length(APathTranslated) - Length(APathInfo)), Length(APathInfo));

      Result := APathTranslated + AScriptName + CPathDivider;
    end;
  end;
end;

function TMainWebModule.GetResultString(AAction: TValueAction; AResult: String) : String;
const
  CEverythingFine   = 'everything went fine';
  CForward          = '(forward)';
  CInternalError    = 'an internal error occured';
  CMissingArguments = 'missing arguments';
  CNoSuchDomain     = 'there is no such domain';
  CPassword         = '(password)';
  CUnknownError     = 'an unknown error occured';
  CWrongCredentials = 'wrong credentials have been provided';
var
  LError  : LongInt;
  LResult : Byte;
begin
  Result := '';

  AResult := Trim(AResult);
  Val(AResult, LResult, LError);
  if (LError = 0) then
  begin
    case AAction of
      vaForward :
      begin
        case LResult of
          0 : Result := AResult + #32 + CEverythingFine + #32 + CForward;
          1 : Result := AResult + #32 + CWrongCredentials + #32 + CForward;
          2 : Result := AResult + #32 + CWrongCredentials + #32 + CForward;
          3 : Result := AResult + #32 + CNoSuchDomain + #32 + CForward;
          4 : Result := AResult + #32 + CWrongCredentials + #32 + CForward;
          5 : Result := AResult + #32 + CInternalError + #32 + CForward;
          6 : Result := AResult + #32 + CMissingArguments + #32 + CForward;
        else
          Result := AResult + #32 + CUnknownError + #32 + CForward;
        end;
      end;

      vaPassword :
      begin
        case LResult of
          0 : Result := AResult + #32 + CEverythingFine + #32 + CPassword;
          1 : Result := AResult + #32 + CWrongCredentials + #32 + CPassword;
          2 : Result := AResult + #32 + CWrongCredentials + #32 + CPassword;
          3 : Result := AResult + #32 + CNoSuchDomain + #32 + CPassword;
          4 : Result := AResult + #32 + CWrongCredentials + #32 + CPassword;
          5 : Result := AResult + #32 + CInternalError + #32 + CPassword;
          6 : Result := AResult + #32 + CMissingArguments + #32 + CPassword;
        else
          Result := AResult + #32 + CUnknownError + #32 + CPassword;
        end;
      end;
    else
      Result := AResult + #32 + CUnknownError;
    end;
  end
  else
    Result := AResult + #32 + CUnknownError;
end;

function TMainWebModule.GetWindowsPath: String;
var
  LSize : Integer;
begin
  Result := '';

  LSize := GetWindowsDirectory(nil, 0);
  if (LSize > 0) then
  begin
    SetLength(Result, Succ(LSize));
    GetWindowsDirectory(@Result[1], Length(Result));

    Result := Trim(Result);
    if (Length(Result) > 0) then
    begin
      if (Result[Length(Result)] <> CPathDivider) then
        Result := Result + CPathDivider;
    end;
  end;
end;

procedure TMainWebModule.MainWebModuledefaultAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  LCGIDirectory : String;
  LParameters   : TStringList;
  LTextResult   : String;
  LValueRecord  : TValueRecord;
begin
  LCGIDirectory := GetCGIPath(Request.PathTranslated, Request.PathInfo, Request.ScriptName);
  if DirectoryExists(LCGIDirectory) then
  begin
    LValueRecord := ParseRequest(Request);

    case LValueRecord.Action of
      vaForward :
      begin
        if FileExists(LCGIDirectory + CForwardVBS) then
        begin
          LParameters := TStringList.Create;
          try
            LParameters.Add(CNoLogoParam);
            LParameters.Add(LCGIDirectory + CForwardVBS);
            if (Length(Trim(LValueRecord.Domain)) > 0) then
              LParameters.Add(LValueRecord.Domain);
            if (Length(Trim(LValueRecord.Username)) > 0) then
              LParameters.Add(LValueRecord.Username);
            if (Length(Trim(LValueRecord.Password)) > 0) then
              LParameters.Add(LValueRecord.Password);
            if (Length(Trim(LValueRecord.ForwardAddress)) > 0) then
              LParameters.Add(LValueRecord.ForwardAddress);

            LTextResult := Trim(GetApplicationOutput(GetWindowsPath + CCscriptExe, LParameters));
            Response.Content := GetResultString(LValueRecord.Action, LTextResult);
          finally
            LParameters.Free;
          end;
        end;
      end;

      vaPassword :
      begin
        if FileExists(LCGIDirectory + CPasswordVBS) then
        begin
          LParameters := TStringList.Create;
          try
            LParameters.Add(CNoLogoParam);
            LParameters.Add(LCGIDirectory + CPasswordVBS);
            if (Length(Trim(LValueRecord.Domain)) > 0) then
              LParameters.Add(LValueRecord.Domain);
            if (Length(Trim(LValueRecord.Username)) > 0) then
              LParameters.Add(LValueRecord.Username);
            if (Length(Trim(LValueRecord.Password)) > 0) then
              LParameters.Add(LValueRecord.Password);
            if (Length(Trim(LValueRecord.NewPassword)) > 0) then
              LParameters.Add(LValueRecord.NewPassword);

            LTextResult := Trim(GetApplicationOutput(GetWindowsPath + CCscriptExe, LParameters));
            Response.Content := GetResultString(LValueRecord.Action, LTextResult);
          finally
            LParameters.Free;
          end;
        end;
      end;
    else
      Response.Content := '';
    end;
  end;

  Handled := true;
end;

function TMainWebModule.ParseRequest(ARequest: TWebRequest) : TValueRecord;
var
  LAction     : String;
  LIndex      : Integer;
  LScriptName : String;
  LUsername   : String;
begin
  Result.Action         := vaUnknown;
  Result.Domain         := '';
  Result.Username       := '';
  Result.Password       := '';
  Result.ForwardAddress := '';
  Result.NewPassword    := '';

  if (ARequest <> nil) then
  begin
    LScriptName := AnsiLowerCase(Trim(ARequest.ScriptName));
    if (Length(LScriptName) > 0) then
    begin
      // kill trailing slash
      if (LScriptName[Length(LScriptName)] = CURLDivider) then
        Delete(LScriptName, Length(LScriptName), 1);

      // read trailing value - is username
      // kill trailing value
      for LIndex := Length(LScriptName) downto 1 do
      begin
        if (LScriptName[LIndex] = CURLDivider) then
        begin
          LUserName := Copy(LScriptName, Succ(LIndex), Length(LScriptName) - LIndex);
          Delete(LScriptName, LIndex, Length(LScriptName) - Pred(LIndex));

          Break;
        end;
      end;

      // read trailing value - is action
      // kill trailing value
      for LIndex := Length(LScriptName) downto 1 do
      begin
        if (LScriptName[LIndex] = CURLDivider) then
        begin
          LAction := Copy(LScriptName, Succ(LIndex), Length(LScriptName) - LIndex);
          Delete(LScriptName, LIndex, Length(LScriptName) - Pred(LIndex));

          Break;
        end;
      end;

      // check action string
      if AnsiSameText(LAction, CForwardAction) then
        Result.Action := vaForward;
      if AnsiSameText(LAction, CPasswordAction) then
        Result.Action := vaPassword;

      case Result.Action of
        vaForward :
        begin
          // read "forward" parameter
          LIndex := Request.QueryFields.IndexOfName(CForwardParam);
          if (LIndex >= 0) then
            Result.ForwardAddress := Request.QueryFields.ValueFromIndex[LIndex];

          // read "password" parameter
          LIndex := Request.QueryFields.IndexOfName(CPasswordParam);
          if (LIndex >= 0) then
            Result.Password := Request.QueryFields.ValueFromIndex[LIndex];

          Result.Domain   := Request.Host;
          Result.Username := LUserName;
        end;

        vaPassword :
        begin
          // read "new" parameter
          LIndex := Request.QueryFields.IndexOfName(CNewParam);
          if (LIndex >= 0) then
            Result.NewPassword := Request.QueryFields.ValueFromIndex[LIndex];

          // read "password" parameter
          LIndex := Request.QueryFields.IndexOfName(CPasswordParam);
          if (LIndex >= 0) then
            Result.Password := Request.QueryFields.ValueFromIndex[LIndex];

          Result.Domain   := Request.Host;
          Result.Username := LUserName;
        end;
      end;
    end;
  end;
end;

end.

In dem Programm selber gehen wir folgendermaßen vor:

  1. Wir parsen den URL-String.
  2. Wir führen das entsprechende VBScript aus.
  3. Wir lesen den Antwortwert aus stdout.
  4. Wir geben den erhaltenen Ergebniscode zurück.

Für den Zugriff habe ich mir ein tolles URL-Schema überlegt, das eingehalten werden muss, damit das Programm die richtigen Daten auslesen kann. Folgende Befehle gelten für das Setzen einer Mailumleitung:

1
2
https://[domain]/cgi-bin/[exedatei]/forward/[username]?password=[password]
https://[domain]/cgi-bin/[exedatei]/forward/[username]?password=[password]&forward=[email]

Durch das Setzen des forward-Parameters wird die Umleitung aktiviert. Durch Weglassen des Parameters wird die Umleitung deaktiviert. :-)

Für das Ändern des Passworts wird folgendes URL-Schema benutzt. Wenn man ein Mail-Forwarding einrichten kann, dann kann man auch sein Passwort ändern – die Befehle unterscheiden sich nur geringfügig:

1
https://[domain]/cgi-bin/[exedatei]/password]/[username]?password=[password]&new=[newpassword]

Was haltet ihr von dieser Form der Implementierung? Denkt ihr, ein Benutzer kann sich zwei URLs merke? Wie findet ihr den Lösungsansatz generell?

Update:
Der vorhandene Exploit wurde nun behoben. Das Problem war, dass die übergebenen Parameter nicht überprüft wurden, bevor sie in den auszuführenden Befehl eingebaut wurden. Nun werden Anführungszeichen escaped, um das Hinzufügen von zusätzlichen Parametern und andere Modifikationen des Befehls zu unterbinden (Stichwort Pipes).

Konfigurierende Grüße, Kenny




Natürlich wollen auch Schwangere im Winter warm verpackt und gegen Schnee und Kälte geschützt sein. Winterjacken, speziell hergestellt für Schwangere sind etwas ganz Besonderes und nicht leicht zu erwerben, da man auf die Frage, ob es Winterjacken für Schwangere gibt, häufig nur ein genervtes "Nein, haben wir nicht bekommt. Jetzt ist es wieder so weit Die neue Schwangerschaftsmode ist da! Wunderbar lang geschnitten, um den Bereich um Hüften und Po, sowie den Bauch vor Kälte zu schützen. Alle Modelle sind mit zahlreichen Taschen versehen, sodass Sie auch einige Accessoires für Sie und Ihr Baby einpacken können. Jedes Modell gibt es in mehreren Farben und auch in großen Größen.
Einige Winterjacken haben Kapuzen, die Sie vor jedem Wind schützen und durch ihren raffinierten Schnitt können einige Jacken sogar nach der Schwangerschaft getragen werden, ohne dass etwas auffällt. So kann die Jacke auch später noch verwendet werden und man muss für den nächsten Winter keine neue Jacke kaufen.



06 März 2010 ~ 2 Comments | ÄHNLICHE ARTIKEL

Ein Rechner, mehrere Domains und dann auch noch Zertifikate!

Es ist teilweise unglaublich, wieviele Probleme man bekommen kann, wenn man einen eigenen Server betreiben will. Das fängt bei der Erreichbarkeit an (Stichwort dynamische IP), geht über die Probleme beim E-Mail-Versenden (Stichwort Reverse-DNS) und endet bei den SSL-Zertifikaten.

Die Zertifikate haben es nämlich ganz schön in sich. Aber fangen wir vorne an: Wenn ein Rechner sich mit einem Server über den Domainnamen verbindet, dann funktioniert das, indem der Rechner bei einem DNS-Server anfragt, wie den die IP des Rechners ist, der sich hinter dem Namen versteckt. Der Rechner verbindet sich dann mit der IP und beginnt mit der eigentlichen Kommunikation.
Wenn der Rechner also bei dem Server aufschlägt, hat der Server garkeine Ahnung, dass der Besucher mit einer bestimmten Domain sprechen möchte. Diese Information wird z.B. einem HTTP-Server erst im weiteren Gesprächsverlauf mitgeteilt.

Jetzt kommt aber das große Problem: Wenn ein Rechner sich mit dem Server verbindet und der Rechner eine verschlüsselte SSL-Verbindung haben möchte, dann muss der Server ein passendes Zertifikat zurückliefern. In diesem Zertifikat steht der Domainname, für den das Zertifikat gilt.
Der Stolperstein ist nun, dass der Server das passende Zertifikat zurückgeben muss, obwohl ihm der Rechner noch garnicht verraten hat, welche Domain er überhaupt abrufen will!

Um dieses Problem zu lösen gibt es nun mehrere Möglichkeiten, von denen ich mir einige angeguckt habe und die ich mal ein bisschen näher erklären will.

Fangen wir mal mit der Lösung an, die wohl die wenigstens Haushalte “einfach so” aus dem Stehgreif lösen können: Dem Rechner mehrere IPs zuweisen. Hintergrund ist, dass einer Netzwerkkarte mehrere IPs zugewiesen werden können. Anhand der IP, an die die Abfrage gerichtet ist, kann der Server nun herausfinden, welche Domain erfragt werden soll. Je nachdem wird dann einfach das passende Zertifikat verteilt.
Die Lösung ist für den Privathaushalt deshalb unpraktikabel, da man als Privatperson von seinem Internetanbieter meist nur eine einzige IP zugewiesen bekommt. Es gibt zwar die Möglichkeit, sich z.B. bei Manitu mehrere IPs zu besorgen, jedoch bekommt man dann das Problem, dass man einen professionellen Router benötigt, der in der Lage ist, mehrere öffentliche IPs zu verwalten.

Deshalb habe ich lange daran gedacht, folgende Lösung umzusetzen: Ich besorge mir eine Domain, über die sämtliche verschlüsselte Kommunikation abläuft. Wenn jemand SSL benutzen möchte und keine Programmwarnungen sehen will, soll er halt über diese designierte Domain gehen, da diese dann in den Zertifikaten steht. Das einzige Problem wäre, dass ein Benutzer sich evtl. wundert, dass er von einer Webseite auf eine komplett andere Domain weitergeleitet wird. Alles andere könnte man durch korrekte Konfiguration und durch Programmierung lösen.

Für viele würde diese Variante sicherlich ausreichen – ich persönlich finde sie allerdings ziemlich unschön. Also habe ich weitergesucht und habe die TLS-Erweiterung namens SNI gefunden. Diese löst genau das oben Problem: Bei der Verwendung von SNI wird bereits beim Aufbau der verschlüsselten Verbindung mitgeteilt, welche Domain angesprochen werden soll. Dadurch kann der Server das passende Zertifikat heraussuchen und die Verbindung korrekt herstellen. An sich eine gute Lösung…

…wenn denn der Server es nicht explizit unterstützen müsste. Leider gibt es immernoch einige Serversoftware, die ausschließlich SSL (bzw. TLS 1.0) unterstützen. Deshalb habe ich noch weiter gesucht und bin schlussendlich beim X.509-Standard gelandet. Dieser beschreibt, welche Werte in einem Zertifikat stehen. Auch dieser hat in letzter eine Veränderung erfahren, die für unsere Belange genutzt werden kann: Gemeint ist die Erweiterung SubjectAltName.
Diese sieht vor, dass man ein Zertifikat erstellen kann, das für mehrere Domains gültig ist – nämlich genau für die, die im Feld SubjectAltName hinterlegt sind.
Der Vorteil ist, dass dieses Vorgehen mit allen Servern funktioniert – auch, wenn sie die Erweiterung nicht unterstützen. Lediglich der Client muss sie auswerten können. Sollte es nun einen Client geben, der lediglich den Common Name (CN) auswertet, so kann man für diesen zumindest noch zusätzlich die oben erwähnte Variante mit der separaten SSL-Domain anbieten.

Abschließend wollte ich euch noch zeigen, wie man relativ einfach ein selbst-signiertes Zertifikat mit integrierten SubjectAltNames erstellen kann. Basis hierfür waren die Artikel auf therowes.net, iiegn.blogspot.com und sial.org.

Wenn man es noch nicht hat, muss man sich zuerst einmal einen privaten Schlüssel erzeugen. Das geht bei OpenSSL mit diesem Befehl:

1
openssl genrsa 2048 > key.rsa

Denkt bitte dran, dass dies der private Schlüssel ist, der im Moment nicht mit einem Passwort geschützt ist. Einige Serverprogramme brauchen ihn in diesem Zustand. Der Schlüssel muss in diesem Zustand unbedingt vor unbefugtem Zugriff geschützt werden!

Als nächstes muss die Datei “openssl.cfg” der OpenSSL-Installation bearbeitet werden. Über den interaktiven Modus von OpenSSL ist es derzeit nicht möglich, die SubjectAltNames zu definieren, deshalb muss dies in der Konfigurationsdatei geschehen.

Guckt zuerst, ob im Bereich “req” der Wert “req_extensions” auf “v3_req” gesetzt ist:

1
2
[ req ]
req_extensions = v3_req # The extensions to add to a certificate request

Als nächtes müsst ihr in den Bereichen “v3_req” und “v3_ca” den Wert “subjectAltName” auf “@subject_alt_names” setzen:

1
2
[ v3_req ]
subjectAltName=@subject_alt_names
1
2
[ v3_ca ]
subjectAltName=@subject_alt_names

Abschließend müsst ihr einen neuen Bereich “subject_alt_names” anlegen und dort die Domains reinschreiben, für die das Zertifikat gelten soll. Vergesst hier auf keinen Fall die Domain, die im Common Name stehen wird!

1
2
3
4
5
[ subject_alt_names ]
DNS.1 = example.com
DNS.2 = *.example.com
DNS.3 = example.net
DNS.4 = *.example.net

Und nun liegt es an euch, auf welchem Weg ihr das Zertifikat erstellen wollt. Wenn ihr über einen CSR gehen wollt, wird die Einstellung im Bereich “v3_req” verwendet. Wenn ihr jedoch gleich selbst signiert, sind die Einstellung in “v3_ca” aktiv.

Ich persönlich habe den zweiten Weg genommen, da ich mir damit das Erstellen einer eigenen CA gespart habe. Der Befehl für den direkten Weg lautet wie folgt:

1
openssl req -new -x509 -nodes -sha1 -days 365 -key key.rsa > key.rsa.cert

Beim Ausführen des Befehls werdet ihr nach den Werten für das Zertifikat gefragt. Vergesst nicht, den Common Name richtig zu setzen. Das dabei herauskommende Zertifikat wird 365 Tage lang gültig sein. Dieses Zertifikat muss übrigens nicht (im Gegensatz zum privaten Schlüssel) gesondert gesichert werden – es wird sowieso vom Server an jeden geschickt, der danach fragt! :D

Sooo… meine Güte! Der Artikel ist länger geworden, als ich gedacht hatte :D ! Ich muss aber auch zugeben, dass ich ihn eine ganz schöne Zeit vor mir hergeschoben habe und er zeitweise zu einer reinen internen Referenzsammlung verkommen war ;-) . Der Artikel ist auch für mich wichtig, damit ich nicht vergesse, welchen Weg ich nun schlussendlich gegangen bin :D !

Wie hat auch der Artikel gefallen? Standet/steht ihr auch von dem Problem? Wie habt ihr es gelöst? Wäre der gezeigte Weg eine Lösung, die ihr in Betracht ziehen würdet?

Verschlüsselnde Grüße, Kenny

03 März 2010 ~ 4 Comments | ÄHNLICHE ARTIKEL

Mail-Forwarding des hMailServer ändern…

Und noch immer sitze ich am Webserver. Nachdem wir gestern entschieden haben, dass ich die Domains des Bezirks hosten werde, muss ich natürlich dafür sorgen, dass die Leute später nicht nur ihr Passwort ändern können, sondern, dass sie zumindest auch eine Mail-Umleitung einrichten können, falls sie nicht extra ein zusätzliches Postfach abrufen möchten.

Dabei ist wieder ein VBScript herausgekommen, das per cscript.exe aufgerufen werden muss. Es basiert großteilig auf dem Script, mit dem der Benutzer sein Passwort ändern kann.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  ' Wscript.Arguments(0) => domain name
 ' Wscript.Arguments(1) => username
 ' Wscript.Arguments(2) => password
 ' Wscript.Arguments(3) => forwarding mail address

  On Error Resume Next
  Err.Clear
 
  If Wscript.Arguments.Count = 3 Or Wscript.Arguments.Count = 4 Then
    Dim obApp
    Set obApp = CreateObject("hMailServer.Application")

    If Err.Number = 0 Then
      Call obApp.Authenticate(Wscript.Arguments(1) & "@" & Wscript.Arguments(0), Wscript.Arguments(2))
   
      If Err.Number = 0 Then
        Dim obDomain
        Set obDomain = obApp.Domains.ItemByName(Wscript.Arguments(0))
   
        If Err.Number = 0 Then
          Dim obAccount
          Set obAccount = obDomain.Accounts.ItemByAddress(Wscript.Arguments(1) & "@" & Wscript.Arguments(0))
   
          If Err.Number = 0 Then
            If obAccount.ValidatePassword(Wscript.Arguments(2)) Then
              If Wscript.Arguments.Count = 4 Then ' activate forwarding
               obAccount.ForwardAddress      = Wscript.Arguments(3)
                obAccount.ForwardEnabled      = true
                obAccount.ForwardKeepOriginal = false
              Else ' deactivate forwarding
               obAccount.ForwardAddress      = ""
                obAccount.ForwardEnabled      = false
                obAccount.ForwardKeepOriginal = true
              End If
              obAccount.Save

              WScript.StdOut.WriteLine "0" ' forwarding set
           Else
              WScript.StdOut.WriteLine "1" ' password is not correct
           End If
          Else
            WScript.StdOut.WriteLine "2" ' account does not exist
         End If
        Else
          WScript.StdOut.WriteLine "3" ' domain does not exist
       End If
      Else
        WScript.StdOut.WriteLine "4" ' password is not correct
      End If
    Else
      WScript.StdOut.WriteLine "5" ' hMailServer.Application not available
   End If
  Else
    WScript.StdOut.WriteLine "6" ' wrong number of arguments
 End If

Ich denke mal, dass damit die beiden wichtigsten Konfigurationsmöglichkeiten für eine E-Mail-Adresse abgedeckt werden sollten. :-)

Habt ihr eine Idee, was man als Endanwender sonst noch konfigurieren können sollte? Wie würdet ihr solch eine Konfiguration am liebsten vornehmen? Würde euch Telnet ausreichen oder würdet ihr eine Webseite bevorzugen?

Umleitende Grüße, Kenny

03 März 2010 ~ 0 Comments | ÄHNLICHE ARTIKEL

Die Vorratsdatenspeicherung ist nichtig… vorerst…

Ja, gestern war in der Tat ein schöner Tag, denn das Bundesverfassungsgericht hat unter Leitung von Hans-Jürgen Papier die derzeitige Form der verdachtsunabhängigen Vorratsdatenspeicherung für verfassungswidrig erklärt.

Ganz herzlich bedanken möchte ich mich an dieser Stelle zunächst beim AK Vorratsdatenspeicherung, der seit Jahren gegen diesen bürgerfeindlichen Überwachungsapparat gekämpft hat und ich unterstütze auch ihr Anliegen, die Vorratsdatenspeicherung nun europaweit abzuschaffen. Wie es scheint kommt da ja bereits ein bisschen Bewegung ins Spiel.

In der Pressemitteilung des Bundesverfassungsbericht zum Urteil über die Vorratsdatenspeicherung werden u.a. Hürden aufgezählt, die erfüllt sein müssten, damit die Speicherung verfassungskonform ist. Leider stürzen sich darauf im Moment die Politiker und überlegen bereits, wie man die Anforderungen so schnell wie möglich in einen tatsächlichen Gesetzestext überführen kann – selbst das BKA ist sich nicht zu schade, zur Eile zu drängen.

Meiner Meinung nach ist das angestrebte Vorgehen totaler Schwachsinn! Das muss man sich mal auf der Zunge zergehen lassen: Das Bundesverfassungsgericht schreibt vor, was maximal möglich ist, um nicht gegen das Grundgesetz zu verstoßen. Und was tun die Politiker? Sie nehmen diese Maximalwerte und wollen sie als Basis für das neue Gesetz verwenden. So nach dem Motto “Wenn wir das Grundgesetz nicht brechen dürfen, dann können wir es wenigstens möglichst weit ausreizen!”

Einer der Vorreiter ist natürlich mal wieder Wolfgang Bosbach, der auch mir schon mehrfach negativ aufgefallen ist. In einer Debatte auf Phoenix hat dieser gestern abgesondert, dass man nun völlig hilflos sei und die Verbrechen nicht mehr ordentlich aufklären könne. Auch die Terrorabwehr wäre so nicht mehr möglich. Er geht sogar soweit, zu behaupten, dass Kriminelle nun dazu ermutigt werden könnten, ihre Verbrechen in Deutschland zu planen, da sie hier ja keine Verfolgung mehr fürchten müssen.
Bei solchen unqualifizierten und völlig haltlosen Behauptungen frage ich mich manchmal wirklich, ob sie diese ahnungslosen Freizeitpolitiker nur für die Kontroverse mit in die Sendung nehmen. Ich kann mir jedenfalls nicht vorstellen, dass sie aufgrund seines “Sachverstandes” auf ihn aufmerksam geworden sind.
Aber okay, wenn ich mich noch weiter über das Nichtwissen von anderen Politikern aufrege, platzt mir evtl. irgendwann nochmal die Hutschnur!

Was zu sagen bleibt, ist, dass wir noch längst nicht am Ziel angekommen sind – die Vorratsdatenspeicherung ist aufgeschoben, aber nach dem Willen der rechten “konservativen” Politiker noch längst nicht aufgehoben. Wir müssen also weiterkämpfen; für die Bürgerrechte; für die Privatsphäre jedes einzelnen Menschen in Deutschland. Das heißt aber auch für mich, dass die Piratenpartei noch mehr als genug Arbeit haben wird, um Deutschland zu dem zu machen, was das Grundgesetz uns zusichert.

Gespeicherte Grüße, Kenny

25 Februar 2010 ~ 3 Comments | ÄHNLICHE ARTIKEL

Der wahre Hacker…

Hacker sind auch heute noch ein Mythos. Das liegt wahrscheinlich daran, dass außenstehende nicht verstehen, was diese Leute machen und wie sie es anstellen. Leider gibt es auch solche Exemplare, die zwar keine Ahnung haben, aber trotzdem unbedingt als Hacker angesehen werden wollen. Dabei kommen dann solche Perlen der Unterhaltung heraus:

Get the Flash Player to see the wordTube Media Player.

Ja, in der Tat war dieses Video des “NextGenHacker101″ Anlass für diesen Blogeintrag: Leute, die Ahnung von der Materie haben, finden das Video einfach nur saukomisch – weil nichts davon korrekt ist. Schlimmerweise könnte sich dieses Nichtwissen bei technikfernen Leuten wirklich festsetzen :-( .
Aber es ist kein Einzelfall: Viele Leute verstehen unter “Hacken” das stupide Einbrechen in einem (Windows-) Computer. Jeder Taschenspielertrick scheint recht zu sein, um die Bezeichnung “Hacker” zu erlangen. Z.B. hier:

Get the Flash Player to see the wordTube Media Player.

Dass dieser “Hack” nur dann funktioniert, wenn kein Administrator-Passwort gesetzt ist, wird dabei einfach mal verschwiegen. Stattdessen hätte man lieber zeigen sollen, wie man über Linux das Admin-Passwort zurücksetzen kann. Aber das wäre wohl zu viel Arbeit gewesen ;-) .
In der gleichen Liga wie die “Ich kann mich in einen ungeschützten Account einloggen”-Hacker spielen die “Ich kann das Passwort ändern, wenn ich bereits eingeloggt bin”-Hacker:

Get the Flash Player to see the wordTube Media Player.

Darüber gibt es dann eigentlich nur noch die Leute, die sich irgendein Programm besorgen und denken, damit jetzt endlich ein Hacker zu sein. Egal, ob es sich dabei dann um normale Remote-Desktop-Tools handelt oder aber um echte, coole “Hacker-Tools” – mit richtig vielen fiesen Knöpfen wie “Vertausche die Maustasten”, “Gib eine Nachricht aus” aus oder anderes Zeug. Ja, in der Tat! Solche Tools könnten auch richtige Hacker benutzen. Nur mit dem Unterschied, dass die auch den schwierigen Part meistern: Nämlich das Client-Programm auf dem Zielrechner zu installieren!

Get the Flash Player to see the wordTube Media Player.

Wie jetzt? Im Video wurde das ganze nur lokal verwendet? Und sogar noch mit Warnhinweis der Firewall? Na das ist ja mal echt gefährlich ;-) .

Aber jetzt mal im Ernst: Wenn das keine Hacker sind, wer ist dann ein Hacker? Dazu gibt es mehrere Antworten – je nachdem, welche Seite man betrachtet. Im Grunde haben jedoch alle Hackerdefinitionen eines gemein: Sie haben ein tiefes Wissen in dem Fachgebiet, auf das sie sich spezialisiert haben.

Grundvoraussetzung um ein richtiger Hacker zu sein ist meiner Meinung nach vor allem das Interesse daran, sich neue Dinge anzueignen, um vorhandene Lösungen verstehen, erforschen und verbessern zu können. Es ist eigentlich völlig egal, ob man sich jetzt nun die Computersicherheit auf die Fahnen geschrieben hat und guckt, wie man Sicherheitsmechanismen umgehen kann – oder, ob man eher technikaffin ist und sich gerne anguckt, wie irgendwelche Geräte intern verdrahtet sind und man solche Hardware gerne mal für den eigenen Gebrauch umbaut.

Es geht dabei garnicht um Ruhm oder Prestige, sondern primär um den eigenen Spieltrieb – herauszufinden, was möglich ist, obwohl es nicht möglich sein sollte; gucken, welche Komponenten man gegen einander ausspielen kann oder um es anders auszudrücken: Gucken, wie weit sich ein System dehnen lässt, bevor es kaputt geht.

Ich persönlich gehe dabei eigentlich immer wie folgt vor: Wenn mir ein neues System (z.B. neue Software) begegnet, gucke ich mir zuerst an, wie sie im Regelfall funktionieren sollte. Wenn ich das verstanden habe, gucke ich mir an, welche Schwachstellen das Gesamtsystem haben könnte (Benutzereingaben, Übergangspunkte zwischen verschiedenen Softwaremodulen, etc.). Anschließend probiere ich aus, wie diese Schwachstellen mit Fehlersituationen umgehen (simple Falscheingaben, gezielte Falscheingaben, usw.). Sollte man nun über ein ungewöhnliches Verhalten stolpern, muss man analysieren, wie es zustande gekommen ist – und man muss überprüfen, ob das Verhalten reproduzierbar und steuerbar ist. Und aus diesen Erkenntnissen baut man dann seine Angriffstragien auf, die dann unter Umständen auch weitere Eingriffe nach sich ziehen können.

Das Vorgehen ist natürlich nur rein exemplarisch – ich bin mir sicher, dass andere Personen auch anders bei ihren Analysen vorgehen.

Wie empfindet ihr gegenüber Hackern? Seid ihr selbst welche? Seht ihr Hacker das absolute Böse? Oder seid ihr solche Fanboys, wie sie in den Videos zu sehen waren? :D

Hackende Grüße, Kenny

21 Februar 2010 ~ 3 Comments | ÄHNLICHE ARTIKEL

SSH + VNC = sichere + Fernwartung

Wie man merkt, steht dieses Wochenende ganz im Zeichen der Informatik-Blogartikel :D . Keine Sorge, es wird auch wieder andere Zeiten geben, aber zur Zeit steht der Homeserver halt hoch im Kurs ;-) . Auch wenn jetzt alle Linux-Fanboys wieder anfangen zu meckern: Der Server läuft unter Windows (XP Pro SP3).

Linuxer werden damit wahrscheinlich kein Problem haben, aber unter Windows ist die Wartung eines Servers ohne grafische Oberfläche ziemlich schwierig, da sich einige Serveranwendungen garnicht über die Kommandozeile administrieren lassen. Eine Lösung musste her!

Mein erster Gedanke war, von außen über VPN einen Tunnels lokale Netzwerk herzustellen und dann im Netzwerk über die Windows-eigene Remotedesktopverbindung auf die grafische Oberfläche zuzugreifen. Das Einrichten von Windows als VPN-Server war überhaupt kein Problem – sowohl die Serverfunktion, als auch die Clientfunktion sind direkt im Betriebssystem enthalten. Allerdings… über den Router wollte das ganze dann nicht mehr funktionieren… trotz etlicher freigegebener Ports :-( .

Zum Glück eilte mir mein Freund @Baschdii (aka. @SBejga) vom rosa Riesen zur Hilfe und erklärte mir, dass man auch über SSH-Verbindung einen Tunnel herstellen kann. Das ist ganz praktisch, denn SSH lief bei mir ja bereits – ich musste in der Konfiguration des Servers (freeSSHd) nur noch die richtige Einstellung finden:

freeSSHd Tunneling

Danach habe ich überlegt, welches Remote-Desktop-Programm ich am besten verwende. Als Protokoll stand VNC relativ schnell fest – doch welche Implementation davon? Es gibt so viele… Nach einigen herben Enttäuschungen (ultraVNC stach dabei als besonders schreckliches Beispiel heraus) habe ich mich dann für realVNC entschieden. Selbst die äußerst eingeschränkte kostenlose Version reicht für meine Zwecke vollkommen aus :D ! Im Vergleich zu einigen Kontrahenten sind sowohl der Server, als auch der Client, ziemlich einfach zu konfigurieren. Um den Server nach außen hin abzusichern, bedarf es einer besonderen Einstellung:

Only accept connections from the local machine


Mit dieser Installation sind die Änderungen am Server bereits abgeschlossen. Nun ist noch der Client an der Reihe. Dieser muss mit 2 Programmen ausgerüstet werden, die man dank der Größe (ca. 6MB) eigentlich immer bei sich haben kann. :-)

Zuerst einmal benötigen wir ein Programm, mit dem wir den SSH-Tunnel zum Server aufbauen können. Für viele dürfte Putty als SSH-Client ein Begriff sein. Baschdii hat mich dagegen auf das Programm Tunnelier aufmerksam gemacht: Und in der Tat! Das Einrichten des Tunnels war ein Klacks :D ! Zuerst einmal trägt man im Login-Tab die SSH-Daten (Host, Port, Benutzername) ein:

Tunnelier: Login-Tab


Danach geht man auf den C2S-Tab und stellt dort den eigentlichen Tunnel ein. Die Konfiguration könnt ihr dem Screenshot entnehmen (Listen Interface = 127.0.0.1, List. Port = 5900, Destination Host = 127.0.0.1, Dest Port = 5900):

Tunnelier: C2S-Tab


Jetzt muss man über Tunnelier nur noch die SSH-Verbindung zum Server herstellen lassen und den realVNC-Client starten. Diesen kann man bereits normal benutzen – mit einer Ausnahme: Als Host muss die Adresse 127.0.0.1 angegeben werden!

realVNC-Client


Nach dem Klick auf “OK” passiert nun folgendes: Der “Viewer” verbindet sich mit dem lokalen Rechner und der Portnummer 5900. Dieser Port wurde von Tunnelier geöffnet und alle Daten, die der Viewer schickt, landen beim SSH-Client. Dieser schickt sie weiter an den SSH-Server, der wiederum bei sich lokal eine Verbindung zum Port 5900 aufgebaut hat – der Port, auf dem der echte realVNC-Server lauscht.
Auf diese Weise werden alle Daten, die die beiden VNC-Anwendungen austauschen über die SSH-Verbindung geleitet. Damit sollte ein Mitlesen oder gar Manipulieren der Daten durch Dritte effektiv verhindert werden :D !

Fernwartende Grüße, Kenny

21 Februar 2010 ~ 0 Comments | ÄHNLICHE ARTIKEL

[Update] Benutzerpasswort des hMailServer ändern…

Eigentlich wollte ich ja Mercury/32 auf meinem Heimserver einsetzen, jedoch hat das Programm mich maßlos enttäuscht. Als Ersatz habe ich für den hMailServer entschieden, der stabil läuft und obendrein auch noch wesentlich einfacher zu konfigurieren ist.

Die Sache funktioniert sogar so gut, dass ich überlege, ob ich nicht weitere Domains auf meinen Heimserver umziehen soll. Bei diesen anderen Domains würden allerdings auch externe Benutzer mit dabei sein: Und genau die würden Probleme machen! :-(

Nein, nicht was ihr denkt :D ! Wer sich die E-Mail-Protokolle (IMAP, POP3, SMTP) schonmal angegeguckt hat, der wird eins feststellen können: Ein Benutzer kann nicht einfach sein Passwort ändern!
Bei den großen Webmail-Anbietern kann man das über deren Webseite machen – das ist ein Zusatzfeature und von Betreiber zu Betreiber unterschiedlich. Doch wie wird das bei mir möglich sein? Eine Lösung musste her! Und die sieht derzeit so aus:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  ' Wscript.Arguments(0) => domain name
 ' Wscript.Arguments(1) => username
 ' Wscript.Arguments(2) => old password
 ' Wscript.Arguments(3) => new password

  On Error Resume Next
  Err.Clear
 
  If Wscript.Arguments.Count = 4 Then
    Dim obApp
    Set obApp = CreateObject("hMailServer.Application")

    If Err.Number = 0 Then
      Call obApp.Authenticate(Wscript.Arguments(1) & "@" & Wscript.Arguments(0), Wscript.Arguments(2))
   
      If Err.Number = 0 Then
        Dim obDomain
        Set obDomain = obApp.Domains.ItemByName(Wscript.Arguments(0))
   
        If Err.Number = 0 Then
          Dim obAccount
          Set obAccount = obDomain.Accounts.ItemByAddress(Wscript.Arguments(1) & "@" & Wscript.Arguments(0))
   
          If Err.Number = 0 Then
            If obAccount.ValidatePassword(Wscript.Arguments(2)) Then
              obAccount.Password = Wscript.Arguments(3)  
              obAccount.Save

              WScript.StdOut.WriteLine "0" ' password set
           Else
              WScript.StdOut.WriteLine "1" ' old password is not correct
           End If
          Else
            WScript.StdOut.WriteLine "2" ' account does not exist
         End If
        Else
          WScript.StdOut.WriteLine "3" ' domain does not exist
       End If
      Else
        WScript.StdOut.WriteLine "4" ' old password is not correct
      End If
    Else
      WScript.StdOut.WriteLine "5" ' hMailServer.Application not available
   End If
  Else
    WScript.StdOut.WriteLine "6" ' wrong number of arguments
 End If

Hierbei handelt es sich um ein VBScript, das die COM-API benutzt, die der hMailServer bereitstellt. Dieses Script wird die Basis eines kleinen Servers werden, an dem man sich mit seiner E-Mail-Adresse und seinem Passwort anmelden und sein Passwort ändern können wird. Wie man das ganze mit SSL absichern kann, habe ich euch ja schon gezeigt. ;-)

Ja, für unerfahrene Benutzer könnte das Passwort ändern wirklich eine kleine Herausforderung werden – die geplante grafische Clientanwendung sollte die Hemmschwelle allerdings auf lange Sicht gesehen senken können. :-)

Update:
Mir ist gerade aufgefallen, dass ich eine wichtige Information vergessen habe! Und zwar muss das Script mit dem Windows Script Host cscript.exe aufgerufen werden. Ansonsten funktioniert die Ausgabe des Ergebniscodes nicht korrekt!

E-Mail-Grüße, Kenny

20 Februar 2010 ~ 0 Comments | ÄHNLICHE ARTIKEL

Stunnel: SSL für alles und jeden

Ich setze schon seit ein paar Tagen meinen Server komplett neu auf. Da der Server dieses Mal länger im Einsatz sein wird, habe ich mir natürlich auch überlegt, wie ich die Kiste ordentlich absichern kann. So biete ich zu allen Protokollen (z.B. IMAP, POP3, SMTP) auch die Varianten mit vorgeschaltetem SSL an (z.B. IMAPS, POP3S, SMTPS).

Wenn der Server direkt SSL unterstützt, ist das ganze garkein Problem. Dummerweise gibt es auch welche, bei denen das nicht der Fall ist. Das Projekt Stunnel kann dabei Abhilfe schaffen :D ! Das Programm fungiert als Reverse-Proxy, nimmt SSL-Verbindungen entgegen und leitet die Daten unverschlüsselt an den nichts ahnenden, lokalen Server weiter. :-)

So sehen bei mir z.B. die Definitionen für HTTPS, POP3S, IMAPS und SMTPS aus – alles ganz einfach. Beachtet werden muss nur, dass sich hinter der Portnummer, die unter “connect” angegeben ist, auch wirklich ein Server verbirgt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[https]
accept  = 443
connect = 80

[pop3s]
accept  = 995
connect = 110

[imaps]
accept  = 993
connect = 143

[smtps]
accept  = 465
connect = 25

Bisher konnte ich noch keine größeren Problem festsstellen. Das Programm scheint auch nicht ganz unbekannt zu sein. ;-)

Verschlüsselte Grüße, Kenny

20 Februar 2010 ~ 0 Comments | ÄHNLICHE ARTIKEL

Hooking mit uallCollection

Ich habe die letzten Tage an einem Projekt gearbeitet, für das ich systemweit API-Aufrufe hooken können musste. Leider ist dieses Unterfangen nicht so einfach, wie das, was ich euch letztes Mal präsentiert habe.

Im Internet finden sich einige Lösungen für dieses Problem: Angefangen beim Schreiben von Systemtreibern, bis hin zum Injizieren von Threads in bereits laufende Prozesse. Besonders gut scheint das Paket madHookCode zu sein, das leider weder günstig noch einfach zu haben ist.
Glücklicherweise gibt es die uallCollection, die die gleiche Methode wie das Paket madHookCode zu benutzen scheint. Auch sonst scheinen die beiden Projekte eng miteinander verknüpft zu sein – für den Programmierer, der sich zwischen den beiden Paketen entscheiden muss, ist das jedenfalls ein Vorteil, da die uallCollection komplett kostenlos ist. ;-)

Lediglich das Injizieren neu gestarteter Prozesse beherrscht die uallCollection nicht. Aber wozu hat man 10 gesunde Finger? Man kan sich relativ leicht eine Möglichkeit schaffen, um selber auf neue Prozesse zu reagieren:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
unit ProcessU;

interface

procedure HookProcess;
procedure UnhookProcess;

implementation

uses
  Windows,
  uallHook,
  MainU;

var
  VProcessLib : HMODULE = 0;

  VNewCreateProcessA : function (lpApplicationName: PChar; lpCommandLine: PChar;
                         lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
                         bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
                         lpCurrentDirectory: PChar; const lpStartupInfo: TStartupInfo;
                         var lpProcessInformation: TProcessInformation): BOOL; stdcall;
  VOldCreateProcessA : function (lpApplicationName: PChar; lpCommandLine: PChar;
                         lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
                         bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
                         lpCurrentDirectory: PChar; const lpStartupInfo: TStartupInfo;
                         var lpProcessInformation: TProcessInformation): BOOL; stdcall;
  VNewCreateProcessW : function (lpApplicationName: PWideChar; lpCommandLine: PWideChar;
                         lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
                         bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
                         lpCurrentDirectory: PWideChar; const lpStartupInfo: TStartupInfo;
                         var lpProcessInformation: TProcessInformation): BOOL; stdcall;
  VOldCreateProcessW : function (lpApplicationName: PWideChar; lpCommandLine: PWideChar;
                         lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
                         bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
                         lpCurrentDirectory: PWideChar; const lpStartupInfo: TStartupInfo;
                         var lpProcessInformation: TProcessInformation): BOOL; stdcall;

function CatchCreateProcessA(lpApplicationName: PChar; lpCommandLine: PChar;
  lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
  bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
  lpCurrentDirectory: PChar; const lpStartupInfo: TStartupInfo;
  var lpProcessInformation: TProcessInformation): BOOL; stdcall; forward;
function CatchCreateProcessW(lpApplicationName: PWideChar; lpCommandLine: PWideChar;
  lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
  bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
  lpCurrentDirectory: PWideChar; const lpStartupInfo: TStartupInfo;
  var lpProcessInformation: TProcessInformation): BOOL; stdcall; forward;

function CatchCreateProcessA(lpApplicationName: PChar; lpCommandLine: PChar;
  lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
  bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
  lpCurrentDirectory: PChar; const lpStartupInfo: TStartupInfo;
  var lpProcessInformation: TProcessInformation): BOOL; stdcall;
begin
  Result := VNewCreateProcessA(lpApplicationName, lpCommandLine, lpProcessAttributes,
                               lpThreadAttributes, bInheritHandles, dwCreationFlags,
                               lpEnvironment, lpCurrentDirectory, lpStartupInfo,
                               lpProcessInformation);

  if Result then
    InjectLibrary(lpProcessInformation.dwProcessId, PAnsiChar(GetDLLFileName));
end;
function CatchCreateProcessW(lpApplicationName: PWideChar; lpCommandLine: PWideChar;
  lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
  bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
  lpCurrentDirectory: PWideChar; const lpStartupInfo: TStartupInfo;
  var lpProcessInformation: TProcessInformation): BOOL; stdcall;
begin
  Result := VNewCreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes,
                               lpThreadAttributes, bInheritHandles, dwCreationFlags,
                               lpEnvironment, lpCurrentDirectory, lpStartupInfo,
                               lpProcessInformation);

  if Result then
    InjectLibrary(lpProcessInformation.dwProcessId, PAnsiChar(GetDLLFileName));
end;

procedure HookProcess;
begin
  VProcessLib := GetModuleHandle(KERNEL32);
  if (VProcessLib <> 0) then
  begin
    @VOldCreateProcessA := GetProcAddress(VProcessLib, 'CreateProcessA');
    @VOldCreateProcessW := GetProcAddress(VProcessLib, 'CreateProcessW');

    HookCode(@VOldCreateProcessA, @CatchCreateProcessA, @VNewCreateProcessA);
    HookCode(@VOldCreateProcessW, @CatchCreateProcessW, @VNewCreateProcessW);
  end;
end;

procedure UnhookProcess;
begin
  if (VProcessLib <> 0) then
  begin
    VProcessLib := 0;

    UnhookCode(@VNewCreateProcessA);
    UnhookCode(@VNewCreateProcessW);
  end;
end;

end.

Für alle, die gerne mal sehen wollen, wie man dieses API-Hooking betreibt, habe ich ein einfaches Programm geschrieben. Mit RegRewrite ist es möglich, Einträge in der Registry durch andere Werte zu ersetzen. Sowohl die neuen Werte, als auch die Konfiguration des Programms werden in der Registry abgelegt ;-) . Guckt am besten in den Quelltext, wenn ihr wissen wollt, wie ihr das Programm richtig konfiguriert – es ist, wie gesagt, nur ein kleiner Test gewesen :) .

  RegRewrite (294.5 KiB, 37 hits)

Zum Schluss noch etwas Rechtliches:
Der Autor dieses Programms haftet nicht für Schäden an Soft- oder Hardware oder Vermögensschäden, die durch das Benutzen des Programms entstehen, es sei denn, diese beruhen auf einem grob fahrlässigen oder vorsätzlichen Handeln des Autors, seiner Erfüllungsgehilfen oder seiner gesetzlichen Vertreter.
Für Schäden an der Gesundheit, dem Körper oder dem Leben des Nutzers haftet der Autor uneingeschränkt.


Hookende Grüße, Kenny

18 Februar 2010 ~ 0 Comments | ÄHNLICHE ARTIKEL

Mercury/32 deinstallieren

Ich habe gestern Abend meinen Server neu eingerichtet. Neben freeFTPd und freeSSHd habe ich mich auch dazu entschieden gehabt, Mercury/32 zu installieren. Leider ist das Programm nicht so praktisch, wie ich es mir erhofft hatte.

Deshalb wollte ich es gerade deinstallieren und stand vor einem größeren Problem: Das Programm liefert keine Deinstallationsmöglichkeit mit – auch über Systemsteuerung/Software ist nichts zu machen. Glücklicherweise habe ich im PMail-Wiki eine Deinstallationsanleitung gefunden.

Das einzige, was mich wirklich massiv ärgert: In der Beschreibung heißt es: “There is nothing in the Windows Registry to remove.”Diese Aussage ist nicht korrekt!

In Wirklichkeit legt das Programm beim Starten automatisch die Schlüssel “HKEY_CLASSES_ROOT\Software\Mercury32″, “HKEY_CURRENT_USER\Software\Mercury32″ und “HKEY_USERS\.DEFAULT\Software\Mercury32″ an. Wer keine Lust hat, dass irgendwelcher Datenmüll nach der Deinstallation zurück bleibt, sollte diese Schlüssel problemlos löschen können.

Aber nicht vergessen: Ich hafte nicht für Schäden an Software, Hardware oder für Vermögensschäden, die durch Anwendung dieser Änderungen entstanden sind oder entstehen könnten. ;-)

Mercury-freie Grüße, Kenny

145
no-www.org extra-www.org

Datenbank: 49 Abfragen in 0.7820.782 Sekunden