r15328: Move some functions around, remove dependencies.
[jelmer/samba4-debian.git] / source / rpc_server / samr / samr_password.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    samr server password set/change handling
5
6    Copyright (C) Andrew Tridgell 2004
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (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., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "rpc_server/dcerpc_server.h"
26 #include "rpc_server/common/common.h"
27 #include "rpc_server/samr/dcesrv_samr.h"
28 #include "system/time.h"
29 #include "lib/crypto/crypto.h"
30 #include "ads.h"
31 #include "libcli/ldap/ldap.h"
32 #include "dsdb/samdb/samdb.h"
33 #include "auth/auth.h"
34 #include "rpc_server/samr/proto.h"
35 #include "libcli/auth/libcli_auth.h"
36 #include "db_wrap.h"
37
38 /* 
39   samr_ChangePasswordUser 
40 */
41 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
42                                  struct samr_ChangePasswordUser *r)
43 {
44         struct dcesrv_handle *h;
45         struct samr_account_state *a_state;
46         struct ldb_context *sam_ctx;
47         struct ldb_message **res, *msg;
48         int ret;
49         struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
50         struct samr_Password *lm_pwd, *nt_pwd;
51         NTSTATUS status = NT_STATUS_OK;
52         const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , NULL };
53
54         DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
55
56         a_state = h->data;
57
58         /* basic sanity checking on parameters.  Do this before any database ops */
59         if (!r->in.lm_present || !r->in.nt_present ||
60             !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
61             !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
62                 /* we should really handle a change with lm not
63                    present */
64                 return NT_STATUS_INVALID_PARAMETER_MIX;
65         }
66         if (!r->in.cross1_present || !r->in.nt_cross) {
67                 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
68         }
69         if (!r->in.cross2_present || !r->in.lm_cross) {
70                 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
71         }
72
73         /* To change a password we need to open as system */
74         sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
75         if (sam_ctx == NULL) {
76                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
77         }
78
79         ret = ldb_transaction_start(sam_ctx);
80         if (ret) {
81                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
82                 return NT_STATUS_TRANSACTION_ABORTED;
83         }
84
85         /* fetch the old hashes */
86         ret = gendb_search_dn(sam_ctx, mem_ctx,
87                               a_state->account_dn, &res, attrs);
88         if (ret != 1) {
89                 ldb_transaction_cancel(sam_ctx);
90                 return NT_STATUS_WRONG_PASSWORD;
91         }
92         msg = res[0];
93
94         status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
95         if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
96                 ldb_transaction_cancel(sam_ctx);
97                 return NT_STATUS_WRONG_PASSWORD;
98         }
99
100         /* decrypt and check the new lm hash */
101         D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
102         D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
103         if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
104                 ldb_transaction_cancel(sam_ctx);
105                 return NT_STATUS_WRONG_PASSWORD;
106         }
107
108         /* decrypt and check the new nt hash */
109         D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
110         D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
111         if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
112                 ldb_transaction_cancel(sam_ctx);
113                 return NT_STATUS_WRONG_PASSWORD;
114         }
115         
116         /* check the nt cross hash */
117         D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
118         if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
119                 ldb_transaction_cancel(sam_ctx);
120                 return NT_STATUS_WRONG_PASSWORD;
121         }
122
123         /* check the lm cross hash */
124         D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
125         if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
126                 ldb_transaction_cancel(sam_ctx);
127                 return NT_STATUS_WRONG_PASSWORD;
128         }
129
130         msg = ldb_msg_new(mem_ctx);
131         if (msg == NULL) {
132                 ldb_transaction_cancel(sam_ctx);
133                 return NT_STATUS_NO_MEMORY;
134         }
135
136         msg->dn = ldb_dn_copy(msg, a_state->account_dn);
137         if (!msg->dn) {
138                 ldb_transaction_cancel(sam_ctx);
139                 return NT_STATUS_NO_MEMORY;
140         }
141
142         /* set the password on the user DN specified.  This may fail
143          * due to password policies */
144         status = samdb_set_password(sam_ctx, mem_ctx,
145                                     a_state->account_dn, a_state->domain_state->domain_dn,
146                                     msg, NULL, &new_lmPwdHash, &new_ntPwdHash, 
147                                     True, /* this is a user password change */
148                                     True, /* run restriction tests */
149                                     NULL,
150                                     NULL);
151         if (!NT_STATUS_IS_OK(status)) {
152                 ldb_transaction_cancel(sam_ctx);
153                 return status;
154         }
155
156         /* The above call only setup the modifications, this actually
157          * makes the write to the database. */
158         ret = samdb_replace(sam_ctx, mem_ctx, msg);
159         if (ret != 0) {
160                 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
161                          ldb_dn_linearize(mem_ctx, a_state->account_dn),
162                          ldb_errstring(sam_ctx)));
163                 ldb_transaction_cancel(sam_ctx);
164                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
165         }
166
167         /* And this confirms it in a transaction commit */
168         ret = ldb_transaction_commit(sam_ctx);
169         if (ret != 0) {
170                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
171                          ldb_dn_linearize(mem_ctx, a_state->account_dn),
172                          ldb_errstring(sam_ctx)));
173                 return NT_STATUS_TRANSACTION_ABORTED;
174         }
175
176         return NT_STATUS_OK;
177 }
178
179 /* 
180   samr_OemChangePasswordUser2 
181 */
182 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
183                                      struct samr_OemChangePasswordUser2 *r)
184 {
185         NTSTATUS status;
186         char new_pass[512];
187         uint32_t new_pass_len;
188         struct samr_CryptPassword *pwbuf = r->in.password;
189         struct ldb_context *sam_ctx;
190         const struct ldb_dn *user_dn;
191         int ret;
192         struct ldb_message **res, *mod;
193         const char * const attrs[] = { "objectSid", "lmPwdHash", NULL };
194         struct samr_Password *lm_pwd;
195         DATA_BLOB lm_pwd_blob;
196         uint8_t new_lm_hash[16];
197         struct samr_Password lm_verifier;
198
199         if (pwbuf == NULL) {
200                 return NT_STATUS_WRONG_PASSWORD;
201         }
202
203         /* To change a password we need to open as system */
204         sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
205         if (sam_ctx == NULL) {
206                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
207         }
208
209         ret = ldb_transaction_start(sam_ctx);
210         if (ret) {
211                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
212                 return NT_STATUS_TRANSACTION_ABORTED;
213         }
214
215         /* we need the users dn and the domain dn (derived from the
216            user SID). We also need the current lm password hash in
217            order to decrypt the incoming password */
218         ret = gendb_search(sam_ctx, 
219                            mem_ctx, NULL, &res, attrs,
220                            "(&(sAMAccountName=%s)(objectclass=user))",
221                            r->in.account->string);
222         if (ret != 1) {
223                 ldb_transaction_cancel(sam_ctx);
224                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
225                 return NT_STATUS_WRONG_PASSWORD;
226         }
227
228         user_dn = res[0]->dn;
229
230         status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
231         if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
232                 ldb_transaction_cancel(sam_ctx);
233                 return NT_STATUS_WRONG_PASSWORD;
234         }
235
236         /* decrypt the password we have been given */
237         lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash)); 
238         arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
239         data_blob_free(&lm_pwd_blob);
240         
241         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
242                               &new_pass_len, STR_ASCII)) {
243                 ldb_transaction_cancel(sam_ctx);
244                 DEBUG(3,("samr: failed to decode password buffer\n"));
245                 return NT_STATUS_WRONG_PASSWORD;
246         }
247
248         /* check LM verifier */
249         if (lm_pwd == NULL || r->in.hash == NULL) {
250                 ldb_transaction_cancel(sam_ctx);
251                 return NT_STATUS_WRONG_PASSWORD;
252         }
253
254         E_deshash(new_pass, new_lm_hash);
255         E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
256         if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
257                 ldb_transaction_cancel(sam_ctx);
258                 return NT_STATUS_WRONG_PASSWORD;
259         }
260
261         mod = ldb_msg_new(mem_ctx);
262         if (mod == NULL) {
263                 ldb_transaction_cancel(sam_ctx);
264                 return NT_STATUS_NO_MEMORY;
265         }
266
267         mod->dn = ldb_dn_copy(mod, user_dn);
268         if (!mod->dn) {
269                 ldb_transaction_cancel(sam_ctx);
270                 return NT_STATUS_NO_MEMORY;
271         }
272
273         /* set the password on the user DN specified.  This may fail
274          * due to password policies */
275         status = samdb_set_password(sam_ctx, mem_ctx,
276                                     user_dn, NULL, 
277                                     mod, new_pass, 
278                                     NULL, NULL,
279                                     True, /* this is a user password change */
280                                     True, /* run restriction tests */
281                                     NULL, 
282                                     NULL);
283         if (!NT_STATUS_IS_OK(status)) {
284                 ldb_transaction_cancel(sam_ctx);
285                 return status;
286         }
287
288         /* The above call only setup the modifications, this actually
289          * makes the write to the database. */
290         ret = samdb_replace(sam_ctx, mem_ctx, mod);
291         if (ret != 0) {
292                 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
293                          ldb_dn_linearize(mem_ctx, user_dn),
294                          ldb_errstring(sam_ctx)));
295                 ldb_transaction_cancel(sam_ctx);
296                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
297         }
298
299         /* And this confirms it in a transaction commit */
300         ret = ldb_transaction_commit(sam_ctx);
301         if (ret != 0) {
302                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
303                          ldb_dn_linearize(mem_ctx, user_dn),
304                          ldb_errstring(sam_ctx)));
305                 return NT_STATUS_TRANSACTION_ABORTED;
306         }
307
308         return NT_STATUS_OK;
309 }
310
311
312 /* 
313   samr_ChangePasswordUser3 
314 */
315 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, 
316                                   TALLOC_CTX *mem_ctx,
317                                   struct samr_ChangePasswordUser3 *r)
318 {       
319         NTSTATUS status;
320         char new_pass[512];
321         uint32_t new_pass_len;
322         struct ldb_context *sam_ctx = NULL;
323         const struct ldb_dn *user_dn;
324         int ret;
325         struct ldb_message **res, *mod;
326         const char * const attrs[] = { "ntPwdHash", "lmPwdHash", NULL };
327         struct samr_Password *nt_pwd, *lm_pwd;
328         DATA_BLOB nt_pwd_blob;
329         struct samr_DomInfo1 *dominfo = NULL;
330         struct samr_ChangeReject *reject = NULL;
331         enum samr_RejectReason reason = SAMR_REJECT_OTHER;
332         uint8_t new_nt_hash[16], new_lm_hash[16];
333         struct samr_Password nt_verifier, lm_verifier;
334
335         ZERO_STRUCT(r->out);
336
337         if (r->in.nt_password == NULL ||
338             r->in.nt_verifier == NULL) {
339                 return NT_STATUS_INVALID_PARAMETER;
340         }
341
342         /* To change a password we need to open as system */
343         sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
344         if (sam_ctx == NULL) {
345                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
346         }
347
348         ret = ldb_transaction_start(sam_ctx);
349         if (ret) {
350                 talloc_free(sam_ctx);
351                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
352                 return NT_STATUS_TRANSACTION_ABORTED;
353         }
354
355         /* we need the users dn and the domain dn (derived from the
356            user SID). We also need the current lm and nt password hashes
357            in order to decrypt the incoming passwords */
358         ret = gendb_search(sam_ctx, 
359                            mem_ctx, NULL, &res, attrs,
360                            "(&(sAMAccountName=%s)(objectclass=user))",
361                            r->in.account->string);
362         if (ret != 1) {
363                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
364                 status = NT_STATUS_WRONG_PASSWORD;
365                 goto failed;
366         }
367
368         user_dn = res[0]->dn;
369
370         status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
371         if (!NT_STATUS_IS_OK(status) ) {
372                 goto failed;
373         }
374
375         if (!nt_pwd) {
376                 status = NT_STATUS_WRONG_PASSWORD;
377                 goto failed;
378         }
379
380         /* decrypt the password we have been given */
381         nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
382         arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
383         data_blob_free(&nt_pwd_blob);
384
385         if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
386                               &new_pass_len, STR_UNICODE)) {
387                 DEBUG(3,("samr: failed to decode password buffer\n"));
388                 status = NT_STATUS_WRONG_PASSWORD;
389                 goto failed;
390         }
391
392         if (r->in.nt_verifier == NULL) {
393                 status = NT_STATUS_WRONG_PASSWORD;
394                 goto failed;
395         }
396
397         /* check NT verifier */
398         E_md4hash(new_pass, new_nt_hash);
399         E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
400         if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
401                 status = NT_STATUS_WRONG_PASSWORD;
402                 goto failed;
403         }
404
405         /* check LM verifier */
406         if (lm_pwd && r->in.lm_verifier != NULL) {
407                 E_deshash(new_pass, new_lm_hash);
408                 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
409                 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
410                         status = NT_STATUS_WRONG_PASSWORD;
411                         goto failed;
412                 }
413         }
414
415
416         mod = ldb_msg_new(mem_ctx);
417         if (mod == NULL) {
418                 return NT_STATUS_NO_MEMORY;
419         }
420
421         mod->dn = ldb_dn_copy(mod, user_dn);
422         if (!mod->dn) {
423                 status = NT_STATUS_NO_MEMORY;
424                 goto failed;
425         }
426
427         /* set the password on the user DN specified.  This may fail
428          * due to password policies */
429         status = samdb_set_password(sam_ctx, mem_ctx,
430                                     user_dn, NULL, 
431                                     mod, new_pass, 
432                                     NULL, NULL,
433                                     True, /* this is a user password change */
434                                     True, /* run restriction tests */
435                                     &reason, 
436                                     &dominfo);
437         if (!NT_STATUS_IS_OK(status)) {
438                 goto failed;
439         }
440
441         /* The above call only setup the modifications, this actually
442          * makes the write to the database. */
443         ret = samdb_replace(sam_ctx, mem_ctx, mod);
444         if (ret != 0) {
445                 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
446                          ldb_dn_linearize(mem_ctx, user_dn),
447                          ldb_errstring(sam_ctx)));
448                 status = NT_STATUS_UNSUCCESSFUL;
449                 goto failed;
450         }
451
452         /* And this confirms it in a transaction commit */
453         ret = ldb_transaction_commit(sam_ctx);
454         if (ret != 0) {
455                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
456                          ldb_dn_linearize(mem_ctx, user_dn),
457                          ldb_errstring(sam_ctx)));
458                 status = NT_STATUS_TRANSACTION_ABORTED;
459                 goto failed;
460         }
461
462         return NT_STATUS_OK;
463
464 failed:
465         ldb_transaction_cancel(sam_ctx);
466         talloc_free(sam_ctx);
467
468         reject = talloc(mem_ctx, struct samr_ChangeReject);
469         r->out.dominfo = dominfo;
470         r->out.reject = reject;
471
472         if (reject == NULL) {
473                 return status;
474         }
475         ZERO_STRUCTP(reject);
476
477         reject->reason = reason;
478
479         return status;
480 }
481
482
483 /* 
484   samr_ChangePasswordUser2 
485
486   easy - just a subset of samr_ChangePasswordUser3
487 */
488 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
489                                   struct samr_ChangePasswordUser2 *r)
490 {
491         struct samr_ChangePasswordUser3 r2;
492
493         r2.in.server = r->in.server;
494         r2.in.account = r->in.account;
495         r2.in.nt_password = r->in.nt_password;
496         r2.in.nt_verifier = r->in.nt_verifier;
497         r2.in.lm_change = r->in.lm_change;
498         r2.in.lm_password = r->in.lm_password;
499         r2.in.lm_verifier = r->in.lm_verifier;
500         r2.in.password3 = NULL;
501
502         return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
503 }
504
505
506 /*
507   set password via a samr_CryptPassword buffer
508   this will in the 'msg' with modify operations that will update the user
509   password when applied
510 */
511 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
512                            void *sam_ctx,
513                            const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
514                            TALLOC_CTX *mem_ctx,
515                            struct ldb_message *msg, 
516                            struct samr_CryptPassword *pwbuf)
517 {
518         NTSTATUS nt_status;
519         char new_pass[512];
520         uint32_t new_pass_len;
521         DATA_BLOB session_key = data_blob(NULL, 0);
522
523         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
524         if (!NT_STATUS_IS_OK(nt_status)) {
525                 return nt_status;
526         }
527
528         arcfour_crypt_blob(pwbuf->data, 516, &session_key);
529
530         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
531                               &new_pass_len, STR_UNICODE)) {
532                 DEBUG(3,("samr: failed to decode password buffer\n"));
533                 return NT_STATUS_WRONG_PASSWORD;
534         }
535
536         /* set the password - samdb needs to know both the domain and user DNs,
537            so the domain password policy can be used */
538         return samdb_set_password(sam_ctx, mem_ctx,
539                                   account_dn, domain_dn, 
540                                   msg, new_pass, 
541                                   NULL, NULL,
542                                   False, /* This is a password set, not change */
543                                   True, /* run restriction tests */
544                                   NULL, NULL);
545 }
546
547
548 /*
549   set password via a samr_CryptPasswordEx buffer
550   this will in the 'msg' with modify operations that will update the user
551   password when applied
552 */
553 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
554                               struct ldb_context *sam_ctx,
555                               const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
556                               TALLOC_CTX *mem_ctx,
557                               struct ldb_message *msg, 
558                               struct samr_CryptPasswordEx *pwbuf)
559 {
560         NTSTATUS nt_status;
561         char new_pass[512];
562         uint32_t new_pass_len;
563         DATA_BLOB co_session_key;
564         DATA_BLOB session_key = data_blob(NULL, 0);
565         struct MD5Context ctx;
566
567         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
568         if (!NT_STATUS_IS_OK(nt_status)) {
569                 return nt_status;
570         }
571
572         co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
573         if (!co_session_key.data) {
574                 return NT_STATUS_NO_MEMORY;
575         }
576
577         MD5Init(&ctx);
578         MD5Update(&ctx, &pwbuf->data[516], 16);
579         MD5Update(&ctx, session_key.data, session_key.length);
580         MD5Final(co_session_key.data, &ctx);
581         
582         arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
583
584         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
585                               &new_pass_len, STR_UNICODE)) {
586                 DEBUG(3,("samr: failed to decode password buffer\n"));
587                 return NT_STATUS_WRONG_PASSWORD;
588         }
589
590         /* set the password - samdb needs to know both the domain and user DNs,
591            so the domain password policy can be used */
592         return samdb_set_password(sam_ctx, mem_ctx,
593                                   account_dn, domain_dn, 
594                                   msg, new_pass, 
595                                   NULL, NULL,
596                                   False, /* This is a password set, not change */
597                                   True, /* run restriction tests */
598                                   NULL, NULL);
599 }
600
601