97ccf3067938675c0f1bdf61fb3fa728329a6663
[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 3 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, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "rpc_server/dcerpc_server.h"
25 #include "rpc_server/common/common.h"
26 #include "rpc_server/samr/dcesrv_samr.h"
27 #include "system/time.h"
28 #include "lib/crypto/crypto.h"
29 #include "dsdb/common/flags.h"
30 #include "libcli/ldap/ldap.h"
31 #include "dsdb/samdb/samdb.h"
32 #include "auth/auth.h"
33 #include "rpc_server/samr/proto.h"
34 #include "libcli/auth/libcli_auth.h"
35 #include "util/util_ldb.h"
36 #include "param/param.h"
37
38 /* 
39   samr_ChangePasswordUser 
40 */
41 NTSTATUS dcesrv_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[] = { "dBCSPwd", "unicodePwd" , 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
67         /* To change a password we need to open as system */
68         sam_ctx = samdb_connect(mem_ctx, global_loadparm, system_session(mem_ctx, global_loadparm));
69         if (sam_ctx == NULL) {
70                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
71         }
72
73         ret = ldb_transaction_start(sam_ctx);
74         if (ret) {
75                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
76                 return NT_STATUS_TRANSACTION_ABORTED;
77         }
78
79         /* fetch the old hashes */
80         ret = gendb_search_dn(sam_ctx, mem_ctx,
81                               a_state->account_dn, &res, attrs);
82         if (ret != 1) {
83                 ldb_transaction_cancel(sam_ctx);
84                 return NT_STATUS_WRONG_PASSWORD;
85         }
86         msg = res[0];
87
88         status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
89         if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
90                 ldb_transaction_cancel(sam_ctx);
91                 return NT_STATUS_WRONG_PASSWORD;
92         }
93
94         /* decrypt and check the new lm hash */
95         D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
96         D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
97         if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
98                 ldb_transaction_cancel(sam_ctx);
99                 return NT_STATUS_WRONG_PASSWORD;
100         }
101
102         /* decrypt and check the new nt hash */
103         D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
104         D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
105         if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
106                 ldb_transaction_cancel(sam_ctx);
107                 return NT_STATUS_WRONG_PASSWORD;
108         }
109         
110         /* The NT Cross is not required by Win2k3 R2, but if present
111            check the nt cross hash */
112         if (r->in.cross1_present && r->in.nt_cross) {
113                 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
114                 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
115                         ldb_transaction_cancel(sam_ctx);
116                         return NT_STATUS_WRONG_PASSWORD;
117                 }
118         }
119
120         /* The LM Cross is not required by Win2k3 R2, but if present
121            check the lm cross hash */
122         if (r->in.cross2_present && r->in.lm_cross) {
123                 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
124                 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
125                         ldb_transaction_cancel(sam_ctx);
126                         return NT_STATUS_WRONG_PASSWORD;
127                 }
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         /* setup password modify mods 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                                     NULL,
149                                     NULL);
150         if (!NT_STATUS_IS_OK(status)) {
151                 ldb_transaction_cancel(sam_ctx);
152                 return status;
153         }
154
155         /* The above call only setup the modifications, this actually
156          * makes the write to the database. */
157         ret = samdb_replace(sam_ctx, mem_ctx, msg);
158         if (ret != 0) {
159                 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
160                          ldb_dn_get_linearized(a_state->account_dn),
161                          ldb_errstring(sam_ctx)));
162                 ldb_transaction_cancel(sam_ctx);
163                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
164         }
165
166         /* And this confirms it in a transaction commit */
167         ret = ldb_transaction_commit(sam_ctx);
168         if (ret != 0) {
169                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
170                          ldb_dn_get_linearized(a_state->account_dn),
171                          ldb_errstring(sam_ctx)));
172                 return NT_STATUS_TRANSACTION_ABORTED;
173         }
174
175         return NT_STATUS_OK;
176 }
177
178 /* 
179   samr_OemChangePasswordUser2 
180 */
181 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
182                                      struct samr_OemChangePasswordUser2 *r)
183 {
184         NTSTATUS status;
185         char new_pass[512];
186         uint32_t new_pass_len;
187         struct samr_CryptPassword *pwbuf = r->in.password;
188         struct ldb_context *sam_ctx;
189         struct ldb_dn *user_dn;
190         int ret;
191         struct ldb_message **res, *mod;
192         const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
193         struct samr_Password *lm_pwd;
194         DATA_BLOB lm_pwd_blob;
195         uint8_t new_lm_hash[16];
196         struct samr_Password lm_verifier;
197
198         if (pwbuf == NULL) {
199                 return NT_STATUS_INVALID_PARAMETER;
200         }
201
202         if (r->in.hash == NULL) {
203                 return NT_STATUS_INVALID_PARAMETER;
204         }
205
206         /* To change a password we need to open as system */
207         sam_ctx = samdb_connect(mem_ctx, global_loadparm, system_session(mem_ctx, global_loadparm));
208         if (sam_ctx == NULL) {
209                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
210         }
211
212         ret = ldb_transaction_start(sam_ctx);
213         if (ret) {
214                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
215                 return NT_STATUS_TRANSACTION_ABORTED;
216         }
217
218         /* we need the users dn and the domain dn (derived from the
219            user SID). We also need the current lm password hash in
220            order to decrypt the incoming password */
221         ret = gendb_search(sam_ctx, 
222                            mem_ctx, NULL, &res, attrs,
223                            "(&(sAMAccountName=%s)(objectclass=user))",
224                            r->in.account->string);
225         if (ret != 1) {
226                 ldb_transaction_cancel(sam_ctx);
227                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
228                 return NT_STATUS_WRONG_PASSWORD;
229         }
230
231         user_dn = res[0]->dn;
232
233         status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
234         if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
235                 ldb_transaction_cancel(sam_ctx);
236                 return NT_STATUS_WRONG_PASSWORD;
237         }
238
239         /* decrypt the password we have been given */
240         lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash)); 
241         arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
242         data_blob_free(&lm_pwd_blob);
243         
244         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
245                               &new_pass_len, STR_ASCII)) {
246                 ldb_transaction_cancel(sam_ctx);
247                 DEBUG(3,("samr: failed to decode password buffer\n"));
248                 return NT_STATUS_WRONG_PASSWORD;
249         }
250
251         /* check LM verifier */
252         if (lm_pwd == NULL) {
253                 ldb_transaction_cancel(sam_ctx);
254                 return NT_STATUS_WRONG_PASSWORD;
255         }
256
257         E_deshash(new_pass, new_lm_hash);
258         E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
259         if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
260                 ldb_transaction_cancel(sam_ctx);
261                 return NT_STATUS_WRONG_PASSWORD;
262         }
263
264         mod = ldb_msg_new(mem_ctx);
265         if (mod == NULL) {
266                 ldb_transaction_cancel(sam_ctx);
267                 return NT_STATUS_NO_MEMORY;
268         }
269
270         mod->dn = ldb_dn_copy(mod, user_dn);
271         if (!mod->dn) {
272                 ldb_transaction_cancel(sam_ctx);
273                 return NT_STATUS_NO_MEMORY;
274         }
275
276         /* set the password on the user DN specified.  This may fail
277          * due to password policies */
278         status = samdb_set_password(sam_ctx, mem_ctx,
279                                     user_dn, NULL, 
280                                     mod, new_pass, 
281                                     NULL, NULL,
282                                     true, /* this is a user password change */
283                                     NULL, 
284                                     NULL);
285         if (!NT_STATUS_IS_OK(status)) {
286                 ldb_transaction_cancel(sam_ctx);
287                 return status;
288         }
289
290         /* The above call only setup the modifications, this actually
291          * makes the write to the database. */
292         ret = samdb_replace(sam_ctx, mem_ctx, mod);
293         if (ret != 0) {
294                 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
295                          ldb_dn_get_linearized(user_dn),
296                          ldb_errstring(sam_ctx)));
297                 ldb_transaction_cancel(sam_ctx);
298                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
299         }
300
301         /* And this confirms it in a transaction commit */
302         ret = ldb_transaction_commit(sam_ctx);
303         if (ret != 0) {
304                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
305                          ldb_dn_get_linearized(user_dn),
306                          ldb_errstring(sam_ctx)));
307                 return NT_STATUS_TRANSACTION_ABORTED;
308         }
309
310         return NT_STATUS_OK;
311 }
312
313
314 /* 
315   samr_ChangePasswordUser3 
316 */
317 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, 
318                                   TALLOC_CTX *mem_ctx,
319                                   struct samr_ChangePasswordUser3 *r)
320 {       
321         NTSTATUS status;
322         char new_pass[512];
323         uint32_t new_pass_len;
324         struct ldb_context *sam_ctx = NULL;
325         struct ldb_dn *user_dn;
326         int ret;
327         struct ldb_message **res, *mod;
328         const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
329         struct samr_Password *nt_pwd, *lm_pwd;
330         DATA_BLOB nt_pwd_blob;
331         struct samr_DomInfo1 *dominfo = NULL;
332         struct samr_ChangeReject *reject = NULL;
333         enum samr_RejectReason reason = SAMR_REJECT_OTHER;
334         uint8_t new_nt_hash[16], new_lm_hash[16];
335         struct samr_Password nt_verifier, lm_verifier;
336
337         ZERO_STRUCT(r->out);
338
339         if (r->in.nt_password == NULL ||
340             r->in.nt_verifier == NULL) {
341                 return NT_STATUS_INVALID_PARAMETER;
342         }
343
344         /* To change a password we need to open as system */
345         sam_ctx = samdb_connect(mem_ctx, global_loadparm, system_session(mem_ctx, global_loadparm));
346         if (sam_ctx == NULL) {
347                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
348         }
349
350         ret = ldb_transaction_start(sam_ctx);
351         if (ret) {
352                 talloc_free(sam_ctx);
353                 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
354                 return NT_STATUS_TRANSACTION_ABORTED;
355         }
356
357         /* we need the users dn and the domain dn (derived from the
358            user SID). We also need the current lm and nt password hashes
359            in order to decrypt the incoming passwords */
360         ret = gendb_search(sam_ctx, 
361                            mem_ctx, NULL, &res, attrs,
362                            "(&(sAMAccountName=%s)(objectclass=user))",
363                            r->in.account->string);
364         if (ret != 1) {
365                 /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
366                 status = NT_STATUS_WRONG_PASSWORD;
367                 goto failed;
368         }
369
370         user_dn = res[0]->dn;
371
372         status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
373         if (!NT_STATUS_IS_OK(status) ) {
374                 goto failed;
375         }
376
377         if (!nt_pwd) {
378                 status = NT_STATUS_WRONG_PASSWORD;
379                 goto failed;
380         }
381
382         /* decrypt the password we have been given */
383         nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
384         arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
385         data_blob_free(&nt_pwd_blob);
386
387         if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
388                               &new_pass_len, STR_UNICODE)) {
389                 DEBUG(3,("samr: failed to decode password buffer\n"));
390                 status = NT_STATUS_WRONG_PASSWORD;
391                 goto failed;
392         }
393
394         if (r->in.nt_verifier == NULL) {
395                 status = NT_STATUS_WRONG_PASSWORD;
396                 goto failed;
397         }
398
399         /* check NT verifier */
400         E_md4hash(new_pass, new_nt_hash);
401         E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
402         if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
403                 status = NT_STATUS_WRONG_PASSWORD;
404                 goto failed;
405         }
406
407         /* check LM verifier */
408         if (lm_pwd && r->in.lm_verifier != NULL) {
409                 E_deshash(new_pass, new_lm_hash);
410                 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
411                 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
412                         status = NT_STATUS_WRONG_PASSWORD;
413                         goto failed;
414                 }
415         }
416
417
418         mod = ldb_msg_new(mem_ctx);
419         if (mod == NULL) {
420                 return NT_STATUS_NO_MEMORY;
421         }
422
423         mod->dn = ldb_dn_copy(mod, user_dn);
424         if (!mod->dn) {
425                 status = NT_STATUS_NO_MEMORY;
426                 goto failed;
427         }
428
429         /* set the password on the user DN specified.  This may fail
430          * due to password policies */
431         status = samdb_set_password(sam_ctx, mem_ctx,
432                                     user_dn, NULL, 
433                                     mod, new_pass, 
434                                     NULL, NULL,
435                                     true, /* this is a user password change */
436                                     &reason, 
437                                     &dominfo);
438         if (!NT_STATUS_IS_OK(status)) {
439                 goto failed;
440         }
441
442         /* The above call only setup the modifications, this actually
443          * makes the write to the database. */
444         ret = samdb_replace(sam_ctx, mem_ctx, mod);
445         if (ret != 0) {
446                 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
447                          ldb_dn_get_linearized(user_dn),
448                          ldb_errstring(sam_ctx)));
449                 status = NT_STATUS_UNSUCCESSFUL;
450                 goto failed;
451         }
452
453         /* And this confirms it in a transaction commit */
454         ret = ldb_transaction_commit(sam_ctx);
455         if (ret != 0) {
456                 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
457                          ldb_dn_get_linearized(user_dn),
458                          ldb_errstring(sam_ctx)));
459                 status = NT_STATUS_TRANSACTION_ABORTED;
460                 goto failed;
461         }
462
463         return NT_STATUS_OK;
464
465 failed:
466         ldb_transaction_cancel(sam_ctx);
467         talloc_free(sam_ctx);
468
469         reject = talloc(mem_ctx, struct samr_ChangeReject);
470         r->out.dominfo = dominfo;
471         r->out.reject = reject;
472
473         if (reject == NULL) {
474                 return status;
475         }
476         ZERO_STRUCTP(reject);
477
478         reject->reason = reason;
479
480         return status;
481 }
482
483
484 /* 
485   samr_ChangePasswordUser2 
486
487   easy - just a subset of samr_ChangePasswordUser3
488 */
489 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
490                                   struct samr_ChangePasswordUser2 *r)
491 {
492         struct samr_ChangePasswordUser3 r2;
493
494         r2.in.server = r->in.server;
495         r2.in.account = r->in.account;
496         r2.in.nt_password = r->in.nt_password;
497         r2.in.nt_verifier = r->in.nt_verifier;
498         r2.in.lm_change = r->in.lm_change;
499         r2.in.lm_password = r->in.lm_password;
500         r2.in.lm_verifier = r->in.lm_verifier;
501         r2.in.password3 = NULL;
502
503         return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
504 }
505
506
507 /*
508   set password via a samr_CryptPassword buffer
509   this will in the 'msg' with modify operations that will update the user
510   password when applied
511 */
512 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
513                            void *sam_ctx,
514                            struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
515                            TALLOC_CTX *mem_ctx,
516                            struct ldb_message *msg, 
517                            struct samr_CryptPassword *pwbuf)
518 {
519         NTSTATUS nt_status;
520         char new_pass[512];
521         uint32_t new_pass_len;
522         DATA_BLOB session_key = data_blob(NULL, 0);
523
524         nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
525         if (!NT_STATUS_IS_OK(nt_status)) {
526                 return nt_status;
527         }
528
529         arcfour_crypt_blob(pwbuf->data, 516, &session_key);
530
531         if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
532                               &new_pass_len, STR_UNICODE)) {
533                 DEBUG(3,("samr: failed to decode password buffer\n"));
534                 return NT_STATUS_WRONG_PASSWORD;
535         }
536
537         /* set the password - samdb needs to know both the domain and user DNs,
538            so the domain password policy can be used */
539         return samdb_set_password(sam_ctx, mem_ctx,
540                                   account_dn, domain_dn, 
541                                   msg, new_pass, 
542                                   NULL, NULL,
543                                   false, /* This is a password set, not change */
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                               struct ldb_dn *account_dn, 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                                   NULL, NULL);
598 }
599
600