root / LicensesMercure / LDAP.pas @ 1
Historique | Voir | Annoter | Télécharger (10,006 ko)
1 | 1 | avalancogn | unit LDAP;
|
---|---|---|---|
2 | |||
3 | {
|
||
4 | LDAP Simple Authentification (c)2004 by Paul TOTH <tothpaul@free.fr>
|
||
5 | http://tothpaul.free.fr
|
||
6 | }
|
||
7 | |||
8 | {
|
||
9 | This program is free software; you can redistribute it and/or
|
||
10 | modify it under the terms of the GNU General Public License
|
||
11 | as published by the Free Software Foundation; either version 2
|
||
12 | of the License, or (at your option) any later version.
|
||
13 | |||
14 | This program is distributed in the hope that it will be useful,
|
||
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
17 | GNU General Public License for more details.
|
||
18 | |||
19 | You should have received a copy of the GNU General Public License
|
||
20 | along with this program; if not, write to the Free Software
|
||
21 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
22 | }
|
||
23 | |||
24 | interface
|
||
25 | |||
26 | // http://abcdrfc.free.fr/rfc-vf/rfc2251.html
|
||
27 | |||
28 | uses
|
||
29 | Dialogs,Winsock,SysUtils; |
||
30 | |||
31 | function LDAPOpen(Server:string; Port:integer=389):integer; |
||
32 | function LDAPBind(LDAP:integer; Name,Password:string):boolean; |
||
33 | function LDAPWhoAmI(LDAP:integer):string; |
||
34 | function LDAPCaps(LDAP:integer):string; |
||
35 | function LDAPSearch(LDAP:integer; Name:string):string; |
||
36 | function LDAPAttribute(LDAP:integer; DN,Attribute:string):string; |
||
37 | procedure LDAPClose(var LDAP:integer); |
||
38 | |||
39 | implementation
|
||
40 | |||
41 | const
|
||
42 | LDAP_BOOLEAN =#$01;
|
||
43 | LDAP_INTEGER =#$02;
|
||
44 | LDAP_STRING =#$04;
|
||
45 | LDAP_NULL =#$05;
|
||
46 | LDAP_ENUM =#$0A;
|
||
47 | |||
48 | LDAP_SEQUENCE =#$30;
|
||
49 | LDAP_SET =#$31;
|
||
50 | |||
51 | LDAP_BIND =#$60;
|
||
52 | LDAP_BIND_REPLY =#$61;
|
||
53 | LDAP_SEARCH =#$63;
|
||
54 | LDAP_SEARCH_ENTRY=#$64;
|
||
55 | LDAP_SEARCH_DONE =#$65;
|
||
56 | LDAP_EXTENDED =#$77;
|
||
57 | |||
58 | LDAP_FILTER_ANY =#$87;
|
||
59 | LDAP_FILTER_OR =#$A1;
|
||
60 | LDAP_FILTER_MATCH=#$A3;
|
||
61 | |||
62 | LDAP_FALSE = #$00;
|
||
63 | LDAP_TRUE = #$FF;
|
||
64 | |||
65 | LDAP_WHOAMI = '1.3.6.1.4.1.4203.1.11.3';
|
||
66 | |||
67 | procedure WSAStartup;
|
||
68 | var
|
||
69 | WSA:TWSAData; |
||
70 | begin
|
||
71 | Winsock.WSAStartup($101,WSA);
|
||
72 | end;
|
||
73 | |||
74 | function INetAddr(Host:pansichar):integer;
|
||
75 | var
|
||
76 | PHost:PHostEnt; |
||
77 | begin
|
||
78 | Result:=inet_addr(Host); |
||
79 | if Result=INADDR_NONE then begin |
||
80 | PHost:=gethostbyname(Host); |
||
81 | if PHost=nil then exit; |
||
82 | Result:=integer(pointer(PHost.h_addr^)^); |
||
83 | end;
|
||
84 | end;
|
||
85 | |||
86 | function SendData(Socket:integer; var Data; Size:integer):integer; |
||
87 | var
|
||
88 | p:pchar; |
||
89 | i:integer; |
||
90 | begin
|
||
91 | Result:=Size; |
||
92 | p:=@Data; |
||
93 | while Size>0 do begin |
||
94 | i:=send(Socket,p^,Size,0);
|
||
95 | if i<=0 then begin |
||
96 | Result:=i; |
||
97 | exit; |
||
98 | end;
|
||
99 | dec(Size,i); |
||
100 | inc(p,i); |
||
101 | end;
|
||
102 | end;
|
||
103 | |||
104 | function ReadData(Socket:integer; var Str:string; Min:integer):boolean; |
||
105 | var
|
||
106 | l:integer; |
||
107 | i:integer; |
||
108 | b:array[0..1023] of char; |
||
109 | begin
|
||
110 | l:=Length(Str); |
||
111 | while l<Min do begin |
||
112 | i:=recv(Socket,b,SizeOf(b),0);
|
||
113 | if i<=0 then begin |
||
114 | Result:=False; |
||
115 | exit; |
||
116 | end;
|
||
117 | SetLength(Str,l+i); |
||
118 | Move(b,Str[l+1],i);
|
||
119 | inc(l,i); |
||
120 | end;
|
||
121 | Result:=True; |
||
122 | end;
|
||
123 | |||
124 | function BinToInt(var Data; Size:integer):integer; |
||
125 | var
|
||
126 | b:^byte; |
||
127 | begin
|
||
128 | b:=@Data; |
||
129 | Result:=b^; |
||
130 | while Size>1 do begin |
||
131 | Result:=Result shl 8+b^; |
||
132 | dec(Size); |
||
133 | end;
|
||
134 | end;
|
||
135 | |||
136 | function Chunk(Code:char; Data:string):string; |
||
137 | var
|
||
138 | len :integer; |
||
139 | begin
|
||
140 | len:=Length(Data); |
||
141 | Result:=chr(Len and $FF); |
||
142 | if len>=128 then begin |
||
143 | while Len>255 do begin |
||
144 | Len:=Len shr 8; |
||
145 | Result:=chr(Len and $FF)+Result; |
||
146 | end;
|
||
147 | Result:=chr($80+Length(Result))+Result;
|
||
148 | end;
|
||
149 | Result:=Code+Result+Data; |
||
150 | end;
|
||
151 | |||
152 | function GetChunk(Code:char; var Data,Chunk:string):boolean; |
||
153 | var
|
||
154 | Head,Size:integer; |
||
155 | begin
|
||
156 | Result:=False; |
||
157 | if (Data='')or(Data[1]<>Code) then exit; |
||
158 | Head:=2;
|
||
159 | Size:=ord(Data[Head]); |
||
160 | if Size>=128 then begin |
||
161 | Head:=2+Size and $7F; |
||
162 | Size:=BinToInt(Data[3],Head-2); |
||
163 | end;
|
||
164 | Chunk:=Copy(Data,Head+1,Size);
|
||
165 | Delete(Data,1,Head+Size);
|
||
166 | Result:=True; |
||
167 | end;
|
||
168 | |||
169 | function StrChunk(Var Data:string):string; |
||
170 | begin
|
||
171 | GetChunk(LDAP_STRING,Data,Result); |
||
172 | end;
|
||
173 | |||
174 | function IntChunk(var Data:string):integer; |
||
175 | var
|
||
176 | Chunk:string;
|
||
177 | begin
|
||
178 | if GetChunk(LDAP_INTEGER,Data,Chunk) then |
||
179 | Result:=BinToInt(Chunk[1],Length(Chunk))
|
||
180 | else
|
||
181 | Result:=-1;
|
||
182 | end;
|
||
183 | |||
184 | function EnumChunk(var Data:string):integer; |
||
185 | var
|
||
186 | Chunk:string;
|
||
187 | begin
|
||
188 | if GetChunk(LDAP_ENUM,Data,Chunk) then |
||
189 | Result:=BinToInt(Chunk[1],Length(Chunk))
|
||
190 | else
|
||
191 | Result:=-1;
|
||
192 | end;
|
||
193 | |||
194 | function OpenChunk(Code:char; var Data:string):boolean; |
||
195 | var
|
||
196 | Chunk:string;
|
||
197 | begin
|
||
198 | Result:=False; |
||
199 | Chunk:=Data; |
||
200 | if not GetChunk(Code,Chunk,Data) then exit; |
||
201 | if Chunk<>'' then exit; |
||
202 | Result:=True; |
||
203 | end;
|
||
204 | |||
205 | function ReadSequence(LDAP:integer; var Str,Left:string; Sequence:integer):boolean; |
||
206 | var
|
||
207 | Size:integer; |
||
208 | Head:integer; |
||
209 | begin
|
||
210 | Result:=False; |
||
211 | // minimum Header
|
||
212 | Head:=2;
|
||
213 | if ReadData(LDAP,Str,Head)=False then exit; |
||
214 | // Sequence
|
||
215 | if Str[1]<>LDAP_SEQUENCE then exit; |
||
216 | // Single byte size
|
||
217 | Size:=ord(Str[2]);
|
||
218 | // Multiple byte Size
|
||
219 | if Size>=128 then begin |
||
220 | Head:=2+Size and $7F; |
||
221 | if ReadData(LDAP,Str,Head)=False then exit; |
||
222 | Size:=BinToInt(Str[3],Head-2); |
||
223 | end;
|
||
224 | // Read whole Sequence
|
||
225 | if ReadData(LDAP,Str,Head+Size)=False then exit; |
||
226 | // drop Sequence header
|
||
227 | Delete(Str,1,Head);
|
||
228 | Left:=Copy(Str,Size+1,MaxInt);
|
||
229 | SetLength(Str,Size); |
||
230 | // Skip Sequence number
|
||
231 | if IntChunk(Str)<>Sequence then exit; |
||
232 | // Ok !
|
||
233 | Result:=True; |
||
234 | end;
|
||
235 | |||
236 | function LDAPOpen(Server:string; Port:integer=389):integer; |
||
237 | var
|
||
238 | SockAddr:TSockAddr; |
||
239 | begin
|
||
240 | Result:=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); |
||
241 | if Result=INVALID_SOCKET then exit; |
||
242 | FillChar(SockAddr,SizeOf(SockAddr),0);
|
||
243 | SockAddr.sin_family:=AF_INET; |
||
244 | SockAddr.sin_addr.S_addr:=INetAddr(pansichar(Server)); |
||
245 | SockAddr.sin_port:=htons(Port); |
||
246 | if connect(Result,SockAddr,SizeOf(SockAddr))<>0 then LDAPClose(Result); |
||
247 | end;
|
||
248 | |||
249 | function LDAPBind(LDAP:integer; Name,Password:string):boolean; |
||
250 | var
|
||
251 | s1,s2:string;
|
||
252 | begin
|
||
253 | Result:=False; |
||
254 | s1:=Chunk(LDAP_SEQUENCE, |
||
255 | Chunk(LDAP_INTEGER,#1)+ // Message ID 1 |
||
256 | Chunk(LDAP_BIND, // Bind
|
||
257 | Chunk(LDAP_INTEGER,#3)+ // LDAP version 3 |
||
258 | Chunk(LDAP_STRING,Name)+ // UserName
|
||
259 | Chunk(#$80,Password) // Password |
||
260 | ) |
||
261 | ); |
||
262 | if SendData(LDAP,s1[1],Length(s1))<=0 then exit; |
||
263 | s1:='';
|
||
264 | if not ReadSequence(LDAP,s1,s2,1) then exit; |
||
265 | if s2<>'' then exit; |
||
266 | if (OpenChunk(LDAP_BIND_REPLY,s1)=False) then exit; // Bind Response |
||
267 | Result:=EnumChunk(s1)=0; // OK |
||
268 | //StrChunk(s1); // DN
|
||
269 | //StrChunk(s1); // ResultString
|
||
270 | end;
|
||
271 | |||
272 | function LDAPWhoAmI(LDAP:integer):string; |
||
273 | var
|
||
274 | s1,s2:string;
|
||
275 | begin
|
||
276 | s1:=Chunk(LDAP_SEQUENCE, |
||
277 | Chunk(LDAP_INTEGER,#4)+ // Message ID 4 |
||
278 | Chunk(LDAP_EXTENDED, // Extension
|
||
279 | Chunk(#$80,LDAP_WHOAMI)
|
||
280 | ) |
||
281 | ); |
||
282 | if SendData(LDAP,s1[1],Length(s1))<=0 then exit; |
||
283 | s1:='';
|
||
284 | if not ReadSequence(LDAP,s1,s2,4) then exit; |
||
285 | Result:=s1; |
||
286 | end;
|
||
287 | |||
288 | function LDAPCaps(LDAP:integer):string; |
||
289 | var
|
||
290 | s1,s2,s3:string;
|
||
291 | begin
|
||
292 | s1:=Chunk(LDAP_SEQUENCE, // SEQUENCE
|
||
293 | Chunk(LDAP_INTEGER,#2)+ // Message ID 2 |
||
294 | Chunk(LDAP_SEARCH, // Search
|
||
295 | Chunk(LDAP_STRING,'')+ // BaseObject |
||
296 | Chunk(LDAP_ENUM,#0)+ // Scope 2 = Whole Tree |
||
297 | Chunk(LDAP_ENUM,#3)+ // Aliases 0 = derefaliases |
||
298 | Chunk(LDAP_INTEGER,#0)+ // Size limite |
||
299 | Chunk(LDAP_INTEGER,#0)+ // Time limite |
||
300 | Chunk(LDAP_BOOLEAN,LDAP_FALSE)+ // Type only (boolean)
|
||
301 | Chunk(LDAP_FILTER_ANY,'objectClass')+
|
||
302 | Chunk(LDAP_SEQUENCE, |
||
303 | Chunk(LDAP_STRING,'supportedCapabilities')
|
||
304 | ) |
||
305 | ) |
||
306 | ); |
||
307 | if SendData(LDAP,s1[1],Length(s1))<=0 then exit; |
||
308 | s1:='';
|
||
309 | if not ReadSequence(LDAP,s1,s2,2) then exit; |
||
310 | if not OpenChunk(LDAP_SEARCH_ENTRY,s1) then exit; |
||
311 | if not ReadSequence(LDAP,s2,s3,2) then exit; |
||
312 | if s3<>'' then exit; |
||
313 | if not OpenChunk(LDAP_SEARCH_DONE,s2) then exit; // Search Done |
||
314 | Result:=s1; |
||
315 | end;
|
||
316 | |||
317 | function LDAPSearch(LDAP:integer; Name:string):string; |
||
318 | var
|
||
319 | s1,s2,s3:string;
|
||
320 | begin
|
||
321 | Result:='';
|
||
322 | s1:=Chunk(LDAP_SEQUENCE, // SEQUENCE
|
||
323 | Chunk(LDAP_INTEGER,#2)+ // Message ID 2 |
||
324 | Chunk(LDAP_SEARCH, // Search
|
||
325 | Chunk(LDAP_STRING,'')+ // BaseObject |
||
326 | Chunk(LDAP_ENUM,#2)+ // Scope 2 = Whole Tree |
||
327 | Chunk(LDAP_ENUM,#0)+ // Aliases 0 = derefaliases |
||
328 | Chunk(LDAP_INTEGER,#0)+ // Size limite |
||
329 | Chunk(LDAP_INTEGER,#0)+ // Time limite |
||
330 | Chunk(LDAP_BOOLEAN,LDAP_FALSE)+ // Type only (boolean)
|
||
331 | Chunk(LDAP_FILTER_OR, // Filter = OR
|
||
332 | Chunk(LDAP_FILTER_MATCH, // Filter = Equality Match
|
||
333 | Chunk(LDAP_STRING,'cn')+ // common name |
||
334 | Chunk(LDAP_STRING,Name) |
||
335 | )+ |
||
336 | Chunk(LDAP_FILTER_MATCH, // Filter = Equality Match
|
||
337 | Chunk(LDAP_STRING,'uid')+ // unique id |
||
338 | Chunk(LDAP_STRING,Name) |
||
339 | ) |
||
340 | )+ |
||
341 | Chunk(LDAP_SEQUENCE, |
||
342 | Chunk(LDAP_NULL,'') // NULL |
||
343 | ) |
||
344 | ) |
||
345 | ); |
||
346 | if SendData(LDAP,s1[1],Length(s1))<=0 then exit; |
||
347 | s1:='';
|
||
348 | if not ReadSequence(LDAP,s1,s2,2) then exit; |
||
349 | if not OpenChunk(LDAP_SEARCH_ENTRY,s1) then exit; |
||
350 | if not ReadSequence(LDAP,s2,s3,2) then exit; |
||
351 | if s3<>'' then exit; |
||
352 | if not OpenChunk(LDAP_SEARCH_DONE,s2) then exit; // Search Done |
||
353 | Result:=StrChunk(s1); |
||
354 | end;
|
||
355 | |||
356 | function LDAPAttribute(LDAP:integer; DN,Attribute:string):string; |
||
357 | var
|
||
358 | s1,s2,s3:string;
|
||
359 | begin
|
||
360 | s1:=Chunk(LDAP_SEQUENCE, // SEQUENCE
|
||
361 | Chunk(LDAP_INTEGER,#3)+ // Message ID 2 |
||
362 | Chunk(LDAP_SEARCH, // Search
|
||
363 | Chunk(LDAP_STRING,DN)+ // BaseObject
|
||
364 | Chunk(LDAP_ENUM,#0)+ // Scope 0 = BaseObject |
||
365 | Chunk(LDAP_ENUM,#0)+ // Aliases 0 = derefaliases |
||
366 | Chunk(LDAP_INTEGER,#0)+ // Size limite |
||
367 | Chunk(LDAP_INTEGER,#0)+ // Time limite |
||
368 | Chunk(LDAP_BOOLEAN,LDAP_FALSE)+ // Type only (boolean)
|
||
369 | Chunk(LDAP_FILTER_ANY,'objectClass')+ // (objectClass=*) |
||
370 | Chunk(LDAP_SEQUENCE, // Sub-Sequence
|
||
371 | Chunk(LDAP_STRING,Attribute) |
||
372 | ) |
||
373 | ) |
||
374 | ); |
||
375 | if SendData(LDAP,s1[1],Length(s1))<=0 then exit; |
||
376 | s1:='';
|
||
377 | if not ReadSequence(LDAP,s1,s2,3) then exit; |
||
378 | if not OpenChunk(LDAP_SEARCH_ENTRY,s1) then exit; |
||
379 | if not ReadSequence(LDAP,s2,s3,3) then exit; |
||
380 | if s3<>'' then exit; |
||
381 | if not OpenChunk(LDAP_SEARCH_DONE,s2) then exit; // Search Done |
||
382 | if StrChunk(s1)<>DN then exit; // DN |
||
383 | if not OpenChunk(LDAP_SEQUENCE,s1) then exit; // Sequence of Sequence |
||
384 | if not OpenChunk(LDAP_SEQUENCE,s1) then exit; // Sequence |
||
385 | if UpperCase(StrChunk(s1))<>UpperCase(Attribute) then exit; |
||
386 | if not OpenChunk(LDAP_SET,s1) then exit; // Set of |
||
387 | Result:=StrChunk(s1); |
||
388 | end;
|
||
389 | |||
390 | procedure LDAPClose(var LDAP:integer); |
||
391 | begin
|
||
392 | closesocket(LDAP); |
||
393 | LDAP:=INVALID_SOCKET; |
||
394 | end;
|
||
395 | |||
396 | initialization
|
||
397 | WSAStartup; |
||
398 | finalization
|
||
399 | WSACleanup; |
||
400 | end. |