2 Unix SMB/CIFS mplementation.
6 Copyright (C) Andrew Tridgell 2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "libcli/cldap/cldap.h"
25 #include "libcli/ldap/ldap.h"
26 #include "librpc/gen_ndr/ndr_nbt.h"
27 #include "librpc/gen_ndr/netlogon.h"
28 #include "torture/torture.h"
29 #include "lib/ldb/include/ldb.h"
30 #include "param/param.h"
32 #define CHECK_STATUS(status, correct) torture_assert_ntstatus_equal(tctx, status, correct, "incorrect status")
34 #define CHECK_VAL(v, correct) torture_assert_int_equal(tctx, (v), (correct), "incorrect value");
36 #define CHECK_STRING(v, correct) torture_assert_str_equal(tctx, v, correct, "incorrect value");
38 test netlogon operations
40 static bool test_cldap_netlogon(struct torture_context
*tctx
, const char *dest
)
42 struct cldap_socket
*cldap
;
44 struct cldap_netlogon search
, empty_search
;
45 struct netlogon_samlogon_response n1
;
49 cldap
= cldap_socket_init(tctx
, tctx
->ev
, lp_iconv_convenience(tctx
->lp_ctx
));
52 search
.in
.dest_address
= dest
;
53 search
.in
.dest_port
= lp_cldap_port(tctx
->lp_ctx
);
54 search
.in
.acct_control
= -1;
55 search
.in
.version
= NETLOGON_NT_VERSION_5
| NETLOGON_NT_VERSION_5EX
;
56 search
.in
.map_response
= true;
58 empty_search
= search
;
60 printf("Trying without any attributes\n");
61 search
= empty_search
;
62 status
= cldap_netlogon(cldap
, tctx
, &search
);
63 CHECK_STATUS(status
, NT_STATUS_OK
);
65 n1
= search
.out
.netlogon
;
67 search
.in
.user
= "Administrator";
68 search
.in
.realm
= n1
.data
.nt5_ex
.dns_domain
;
69 search
.in
.host
= "__cldap_torture__";
71 printf("Scanning for netlogon levels\n");
73 search
.in
.version
= i
;
74 printf("Trying netlogon level %d\n", i
);
75 status
= cldap_netlogon(cldap
, tctx
, &search
);
76 CHECK_STATUS(status
, NT_STATUS_OK
);
79 printf("Scanning for netlogon level bits\n");
81 search
.in
.version
= (1<<i
);
82 printf("Trying netlogon level 0x%x\n", i
);
83 status
= cldap_netlogon(cldap
, tctx
, &search
);
84 CHECK_STATUS(status
, NT_STATUS_OK
);
87 search
.in
.version
= NETLOGON_NT_VERSION_5
|NETLOGON_NT_VERSION_5EX
|NETLOGON_NT_VERSION_IP
;
89 status
= cldap_netlogon(cldap
, tctx
, &search
);
90 CHECK_STATUS(status
, NT_STATUS_OK
);
92 printf("Trying with User=NULL\n");
94 search
.in
.user
= NULL
;
95 status
= cldap_netlogon(cldap
, tctx
, &search
);
96 CHECK_STATUS(status
, NT_STATUS_OK
);
97 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, "");
98 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_RESPONSE_EX
);
100 printf("Trying with User=Administrator\n");
102 search
.in
.user
= "Administrator";
103 status
= cldap_netlogon(cldap
, tctx
, &search
);
104 CHECK_STATUS(status
, NT_STATUS_OK
);
106 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, search
.in
.user
);
107 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_USER_UNKNOWN_EX
);
109 search
.in
.version
= NETLOGON_NT_VERSION_5
;
110 status
= cldap_netlogon(cldap
, tctx
, &search
);
111 CHECK_STATUS(status
, NT_STATUS_OK
);
113 printf("Trying with User=NULL\n");
115 search
.in
.user
= NULL
;
116 status
= cldap_netlogon(cldap
, tctx
, &search
);
117 CHECK_STATUS(status
, NT_STATUS_OK
);
118 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, "");
119 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_RESPONSE
);
121 printf("Trying with User=Administrator\n");
123 search
.in
.user
= "Administrator";
124 status
= cldap_netlogon(cldap
, tctx
, &search
);
125 CHECK_STATUS(status
, NT_STATUS_OK
);
127 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, search
.in
.user
);
128 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_USER_UNKNOWN
);
130 search
.in
.version
= NETLOGON_NT_VERSION_5
| NETLOGON_NT_VERSION_5EX
;
132 printf("Trying with a GUID\n");
133 search
.in
.realm
= NULL
;
134 search
.in
.domain_guid
= GUID_string(tctx
, &n1
.data
.nt5_ex
.domain_uuid
);
135 status
= cldap_netlogon(cldap
, tctx
, &search
);
136 CHECK_STATUS(status
, NT_STATUS_OK
);
137 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_USER_UNKNOWN_EX
);
138 CHECK_STRING(GUID_string(tctx
, &search
.out
.netlogon
.data
.nt5_ex
.domain_uuid
), search
.in
.domain_guid
);
140 printf("Trying with a incorrect GUID\n");
141 guid
= GUID_random();
142 search
.in
.user
= NULL
;
143 search
.in
.domain_guid
= GUID_string(tctx
, &guid
);
144 status
= cldap_netlogon(cldap
, tctx
, &search
);
145 CHECK_STATUS(status
, NT_STATUS_NOT_FOUND
);
147 printf("Trying with a AAC\n");
148 search
.in
.acct_control
= ACB_WSTRUST
|ACB_SVRTRUST
;
149 search
.in
.realm
= n1
.data
.nt5_ex
.dns_domain
;
150 status
= cldap_netlogon(cldap
, tctx
, &search
);
151 CHECK_STATUS(status
, NT_STATUS_OK
);
152 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_RESPONSE_EX
);
153 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, "");
155 printf("Trying with a zero AAC\n");
156 search
.in
.acct_control
= 0x0;
157 search
.in
.realm
= n1
.data
.nt5_ex
.dns_domain
;
158 status
= cldap_netlogon(cldap
, tctx
, &search
);
159 CHECK_STATUS(status
, NT_STATUS_OK
);
160 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_RESPONSE_EX
);
161 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, "");
163 printf("Trying with a zero AAC and user=Administrator\n");
164 search
.in
.acct_control
= 0x0;
165 search
.in
.user
= "Administrator";
166 search
.in
.realm
= n1
.data
.nt5_ex
.dns_domain
;
167 status
= cldap_netlogon(cldap
, tctx
, &search
);
168 CHECK_STATUS(status
, NT_STATUS_OK
);
169 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_USER_UNKNOWN_EX
);
170 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, "Administrator");
172 printf("Trying with a bad AAC\n");
173 search
.in
.user
= NULL
;
174 search
.in
.acct_control
= 0xFF00FF00;
175 search
.in
.realm
= n1
.data
.nt5_ex
.dns_domain
;
176 status
= cldap_netlogon(cldap
, tctx
, &search
);
177 CHECK_STATUS(status
, NT_STATUS_OK
);
178 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_RESPONSE_EX
);
179 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, "");
181 printf("Trying with a user only\n");
182 search
= empty_search
;
183 search
.in
.user
= "Administrator";
184 status
= cldap_netlogon(cldap
, tctx
, &search
);
185 CHECK_STATUS(status
, NT_STATUS_OK
);
186 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.dns_domain
, n1
.data
.nt5_ex
.dns_domain
);
187 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, search
.in
.user
);
189 printf("Trying with just a bad username\n");
190 search
.in
.user
= "___no_such_user___";
191 status
= cldap_netlogon(cldap
, tctx
, &search
);
192 CHECK_STATUS(status
, NT_STATUS_OK
);
193 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, search
.in
.user
);
194 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.dns_domain
, n1
.data
.nt5_ex
.dns_domain
);
195 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_USER_UNKNOWN_EX
);
197 printf("Trying with just a bad domain\n");
198 search
= empty_search
;
199 search
.in
.realm
= "___no_such_domain___";
200 status
= cldap_netlogon(cldap
, tctx
, &search
);
201 CHECK_STATUS(status
, NT_STATUS_NOT_FOUND
);
203 printf("Trying with a incorrect domain and correct guid\n");
204 search
.in
.domain_guid
= GUID_string(tctx
, &n1
.data
.nt5_ex
.domain_uuid
);
205 status
= cldap_netlogon(cldap
, tctx
, &search
);
206 CHECK_STATUS(status
, NT_STATUS_OK
);
207 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.dns_domain
, n1
.data
.nt5_ex
.dns_domain
);
208 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, "");
209 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_RESPONSE_EX
);
211 printf("Trying with a incorrect domain and incorrect guid\n");
212 search
.in
.domain_guid
= GUID_string(tctx
, &guid
);
213 status
= cldap_netlogon(cldap
, tctx
, &search
);
214 CHECK_STATUS(status
, NT_STATUS_NOT_FOUND
);
215 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.dns_domain
, n1
.data
.nt5_ex
.dns_domain
);
216 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, "");
217 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_RESPONSE_EX
);
219 printf("Trying with a incorrect GUID and correct domain\n");
220 search
.in
.domain_guid
= GUID_string(tctx
, &guid
);
221 search
.in
.realm
= n1
.data
.nt5_ex
.dns_domain
;
222 status
= cldap_netlogon(cldap
, tctx
, &search
);
223 CHECK_STATUS(status
, NT_STATUS_OK
);
224 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.dns_domain
, n1
.data
.nt5_ex
.dns_domain
);
225 CHECK_STRING(search
.out
.netlogon
.data
.nt5_ex
.user_name
, "");
226 CHECK_VAL(search
.out
.netlogon
.data
.nt5_ex
.command
, LOGON_SAM_LOGON_RESPONSE_EX
);
232 test cldap netlogon server type flags
234 static bool test_cldap_netlogon_flags(struct torture_context
*tctx
,
237 struct cldap_socket
*cldap
;
239 struct cldap_netlogon search
;
240 struct netlogon_samlogon_response n1
;
241 uint32_t server_type
;
243 cldap
= cldap_socket_init(tctx
, tctx
->ev
, lp_iconv_convenience(tctx
->lp_ctx
));
245 printf("Printing out netlogon server type flags:\n");
248 search
.in
.dest_address
= dest
;
249 search
.in
.dest_port
= lp_cldap_port(tctx
->lp_ctx
);
250 search
.in
.acct_control
= -1;
251 search
.in
.version
= NETLOGON_NT_VERSION_5
| NETLOGON_NT_VERSION_5EX
;
252 search
.in
.map_response
= true;
254 status
= cldap_netlogon(cldap
, tctx
, &search
);
255 CHECK_STATUS(status
, NT_STATUS_OK
);
257 n1
= search
.out
.netlogon
;
258 if (n1
.ntver
== NETLOGON_NT_VERSION_5
)
259 server_type
= n1
.data
.nt5
.server_type
;
260 else if (n1
.ntver
== NETLOGON_NT_VERSION_5EX
)
261 server_type
= n1
.data
.nt5_ex
.server_type
;
263 printf("The word is: %i\n", server_type
);
264 if (server_type
& NBT_SERVER_PDC
)
265 printf("NBT_SERVER_PDC ");
266 if (server_type
& NBT_SERVER_GC
)
267 printf("NBT_SERVER_GC ");
268 if (server_type
& NBT_SERVER_LDAP
)
269 printf("NBT_SERVER_LDAP ");
270 if (server_type
& NBT_SERVER_DS
)
271 printf("NBT_SERVER_DS ");
272 if (server_type
& NBT_SERVER_KDC
)
273 printf("NBT_SERVER_KDC ");
274 if (server_type
& NBT_SERVER_TIMESERV
)
275 printf("NBT_SERVER_TIMESERV ");
276 if (server_type
& NBT_SERVER_CLOSEST
)
277 printf("NBT_SERVER_CLOSEST ");
278 if (server_type
& NBT_SERVER_WRITABLE
)
279 printf("NBT_SERVER_WRITABLE ");
280 if (server_type
& NBT_SERVER_GOOD_TIMESERV
)
281 printf("NBT_SERVER_GOOD_TIMESERV ");
282 if (server_type
& NBT_SERVER_NDNC
)
283 printf("NBT_SERVER_NDNC ");
284 if (server_type
& NBT_SERVER_SELECT_SECRET_DOMAIN_6
)
285 printf("NBT_SERVER_SELECT_SECRET_DOMAIN_6");
286 if (server_type
& NBT_SERVER_FULL_SECRET_DOMAIN_6
)
287 printf("NBT_SERVER_FULL_SECRET_DOMAIN_6");
288 if (server_type
& DS_DNS_CONTROLLER
)
289 printf("DS_DNS_CONTROLLER ");
290 if (server_type
& DS_DNS_DOMAIN
)
291 printf("DS_DNS_DOMAIN ");
292 if (server_type
& DS_DNS_FOREST
)
293 printf("DS_DNS_FOREST ");
301 convert a ldap result message to a ldb message. This allows us to
302 use the convenient ldif dump routines in ldb to print out cldap
305 static struct ldb_message
*ldap_msg_to_ldb(TALLOC_CTX
*mem_ctx
, struct ldb_context
*ldb
, struct ldap_SearchResEntry
*res
)
307 struct ldb_message
*msg
;
309 msg
= ldb_msg_new(mem_ctx
);
310 msg
->dn
= ldb_dn_new(msg
, ldb
, res
->dn
);
311 msg
->num_elements
= res
->num_attributes
;
312 msg
->elements
= talloc_steal(msg
, res
->attributes
);
317 dump a set of cldap results
319 static void cldap_dump_results(struct cldap_search
*search
)
321 struct ldb_ldif ldif
;
322 struct ldb_context
*ldb
;
324 if (!search
|| !(search
->out
.response
)) {
328 /* we need a ldb context to use ldb_ldif_write_file() */
329 ldb
= ldb_init(NULL
, NULL
);
332 ldif
.msg
= ldap_msg_to_ldb(ldb
, ldb
, search
->out
.response
);
334 ldb_ldif_write_file(ldb
, stdout
, &ldif
);
341 test cldap netlogon server type flag "NBT_SERVER_DS_DNS_FOREST"
343 static bool test_cldap_netlogon_flag_ds_dns_forest(struct torture_context
*tctx
,
346 struct cldap_socket
*cldap
;
348 struct cldap_netlogon search
;
349 uint32_t server_type
;
350 struct netlogon_samlogon_response n1
;
354 cldap
= cldap_socket_init(tctx
, tctx
->ev
, lp_iconv_convenience(tctx
->lp_ctx
));
356 printf("Testing netlogon server type flag NBT_SERVER_DS_DNS_FOREST: ");
359 search
.in
.dest_address
= dest
;
360 search
.in
.dest_port
= lp_cldap_port(tctx
->lp_ctx
);
361 search
.in
.acct_control
= -1;
362 search
.in
.version
= NETLOGON_NT_VERSION_5
| NETLOGON_NT_VERSION_5EX
;
363 search
.in
.map_response
= true;
365 status
= cldap_netlogon(cldap
, tctx
, &search
);
366 CHECK_STATUS(status
, NT_STATUS_OK
);
368 n1
= search
.out
.netlogon
;
369 if (n1
.ntver
== NETLOGON_NT_VERSION_5
)
370 server_type
= n1
.data
.nt5
.server_type
;
371 else if (n1
.ntver
== NETLOGON_NT_VERSION_5EX
)
372 server_type
= n1
.data
.nt5_ex
.server_type
;
374 if (server_type
& DS_DNS_FOREST
) {
375 struct cldap_search search2
;
376 const char *attrs
[] = { "defaultNamingContext", "rootDomainNamingContext",
378 struct ldb_context
*ldb
;
379 struct ldb_message
*msg
;
381 /* Trying to fetch the attributes "defaultNamingContext" and
382 "rootDomainNamingContext" */
383 ZERO_STRUCT(search2
);
384 search2
.in
.dest_address
= dest
;
385 search2
.in
.dest_port
= lp_cldap_port(tctx
->lp_ctx
);
386 search2
.in
.timeout
= 10;
387 search2
.in
.retries
= 3;
388 search2
.in
.filter
= "(objectclass=*)";
389 search2
.in
.attributes
= attrs
;
391 status
= cldap_search(cldap
, tctx
, &search2
);
392 CHECK_STATUS(status
, NT_STATUS_OK
);
394 ldb
= ldb_init(NULL
, NULL
);
396 msg
= ldap_msg_to_ldb(ldb
, ldb
, search2
.out
.response
);
398 /* Try to compare the two attributes */
399 if (ldb_msg_element_compare(ldb_msg_find_element(msg
, attrs
[0]),
400 ldb_msg_find_element(msg
, attrs
[1])))
415 test generic cldap operations
417 static bool test_cldap_generic(struct torture_context
*tctx
, const char *dest
)
419 struct cldap_socket
*cldap
;
421 struct cldap_search search
;
422 const char *attrs1
[] = { "currentTime", "highestCommittedUSN", NULL
};
423 const char *attrs2
[] = { "currentTime", "highestCommittedUSN", "netlogon", NULL
};
424 const char *attrs3
[] = { "netlogon", NULL
};
426 cldap
= cldap_socket_init(tctx
, tctx
->ev
, lp_iconv_convenience(tctx
->lp_ctx
));
429 search
.in
.dest_address
= dest
;
430 search
.in
.dest_port
= lp_cldap_port(tctx
->lp_ctx
);
431 search
.in
.timeout
= 10;
432 search
.in
.retries
= 3;
434 status
= cldap_search(cldap
, tctx
, &search
);
435 CHECK_STATUS(status
, NT_STATUS_OK
);
437 printf("fetching whole rootDSE\n");
438 search
.in
.filter
= "(objectclass=*)";
439 search
.in
.attributes
= NULL
;
441 status
= cldap_search(cldap
, tctx
, &search
);
442 CHECK_STATUS(status
, NT_STATUS_OK
);
444 if (DEBUGLVL(3)) cldap_dump_results(&search
);
446 printf("fetching currentTime and USN\n");
447 search
.in
.filter
= "(objectclass=*)";
448 search
.in
.attributes
= attrs1
;
450 status
= cldap_search(cldap
, tctx
, &search
);
451 CHECK_STATUS(status
, NT_STATUS_OK
);
453 if (DEBUGLVL(3)) cldap_dump_results(&search
);
455 printf("Testing currentTime, USN and netlogon\n");
456 search
.in
.filter
= "(objectclass=*)";
457 search
.in
.attributes
= attrs2
;
459 status
= cldap_search(cldap
, tctx
, &search
);
460 CHECK_STATUS(status
, NT_STATUS_OK
);
462 if (DEBUGLVL(3)) cldap_dump_results(&search
);
464 printf("Testing objectClass=* and netlogon\n");
465 search
.in
.filter
= "(objectclass2=*)";
466 search
.in
.attributes
= attrs3
;
468 status
= cldap_search(cldap
, tctx
, &search
);
469 CHECK_STATUS(status
, NT_STATUS_OK
);
471 if (DEBUGLVL(3)) cldap_dump_results(&search
);
473 printf("Testing a false expression\n");
474 search
.in
.filter
= "(&(objectclass=*)(highestCommittedUSN=2))";
475 search
.in
.attributes
= attrs1
;
477 status
= cldap_search(cldap
, tctx
, &search
);
478 CHECK_STATUS(status
, NT_STATUS_OK
);
480 if (DEBUGLVL(3)) cldap_dump_results(&search
);
485 bool torture_cldap(struct torture_context
*torture
)
488 const char *host
= torture_setting_string(torture
, "host", NULL
);
490 ret
&= test_cldap_netlogon(torture
, host
);
491 ret
&= test_cldap_netlogon_flags(torture
, host
);
492 ret
&= test_cldap_netlogon_flag_ds_dns_forest(torture
, host
);
493 ret
&= test_cldap_generic(torture
, host
);